TU Wien:Verteilte Systeme LU (Brandic)/JacORB demo WS07

Aus VoWi
Zur Navigation springen Zur Suche springen

Ziel dieser Seite ist es, eine vollstaendige JacORB-Demo zu zeigen.

Disclaimer[Bearbeiten | Quelltext bearbeiten]

Dieser Code kompiliert und funktioniert im WS07. Es ist aber wahrscheinlich das schon naechstes Jahr (WS08) modifikationen noetig sind, wenn die Server-umgebung auch nur irgendwie aktualisiert wird. Auf pasta war im WS07 folgende software installiert:

  • java 1.5.0.2
  • ant 1.6.5
  • junit 3.8

Einleitung[Bearbeiten | Quelltext bearbeiten]

Der unten dargestellte Programmcode repraesentiert einen Server und Client realisiert mittels Corba und mit Transaktionsunterstuetzung in seiner simpelsten Form. Es ist kaum Code ueber dem fuer CORBA relevanten Teil enthalten. Wenn du Fragen zu dieser Demo hast, poste sie doch auf der Diskussionsseite!

build.xml[Bearbeiten | Quelltext bearbeiten]

Die build.xml Datei ist im wesentlichen von der angabe kopiert.

<?xml version="1.0" encoding="UTF-8"?>

<project name="Demo" default="compile" basedir=".">
    
    <!-- This task definition is only necessary for JUnit-1.6.x as installed at the lab servers.
       If you work at home this might not be required. You can then remove this taskdef entry. -->
    <taskdef name="junit" classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTask">
        <classpath>
            <pathelement location="${ant.home}/lib/junit.jar"/>
            <pathelement location="${ant.home}/lib/ant-junit.jar"/>
        </classpath>
    </taskdef>
    
    <property name="src.dir" value="src"/>
    <property name="gen.dir" value="src/gen"/>
    <property name="idl.dir" value="idl"/>
    <property name="build.dir" value="build"/>
    <property name="ns.dir" value="ns"/>
    <property name="ns.dir.location" location="${ns.dir}"/>
    
    <property environment="env"/>
    <property name="jacorb.home" value="${env.JACORB_HOME}"/>
    <property name="ant.home" value="${env.ANT_HOME}"/>
    
    <path id="project.classpath" >
        <pathelement location="${build.dir}"/>
        <fileset dir="${jacorb.home}/lib">
            <include name="jacorb.jar"/>
            <include name="idl.jar"/>
            <include name="logkit-1.2.jar"/>
            <include name="avalon-framework-4.1.5.jar"/>
        </fileset>
    </path>    
    
    <!-- Run the JacORB naming service. -->
    <target name="run-ns">
        <mkdir dir="${ns.dir}"/>
        <java fork="true" 
              classname="org.jacorb.naming.NameServer"
              classpathref="project.classpath">
            <sysproperty key="org.omg.CORBA.ORBClass" value="org.jacorb.orb.ORB"/>
            <sysproperty key="org.omg.CORBA.ORBSingletonClass" value="org.jacorb.orb.ORBSingleton"/>
            <sysproperty key="jacorb.naming.ior_filename" value="${ns.dir.location}/NS_REF"/>
        </java>
    </target>
    
    <!-- Run the JacORB transaction service. -->
    <target name="run-ts">
        <java fork="true" 
              classname="org.jacorb.transaction.TransactionService"
              classpathref="project.classpath">
            <sysproperty key="org.omg.CORBA.ORBClass" value="org.jacorb.orb.ORB"/>
            <sysproperty key="org.omg.CORBA.ORBSingletonClass" value="org.jacorb.orb.ORBSingleton"/>
            <sysproperty key="ORBInitRef.NameService" value="file://${ns.dir.location}/NS_REF"/>
        </java>
    </target>
    
    <!-- Run the JacORB Naming Manager. Only use that if you work at home! -->
<!--    <target name="run-nmg">
        <java fork="true" 
              classname="org.jacorb.naming.namemanager.NameManager"
              classpathref="project.classpath">
            <sysproperty key="org.omg.CORBA.ORBClass" value="org.jacorb.orb.ORB"/>
            <sysproperty key="org.omg.CORBA.ORBSingletonClass" value="org.jacorb.orb.ORBSingleton"/>
            <sysproperty key="ORBInitRef.NameService" value="file://${ns.dir.location}/NS_REF"/>
        </java>
    </target>    
-->    
    <!-- Compile the IDL file. -->
    <target name="idl-compile">
        <java fork="true" 
              classname="org.jacorb.idl.parser"
              classpathref="project.classpath">
            <arg value="-I${jacorb.home}/idl/omg/" />
            <arg line="-d ${gen.dir}" />
<!--	    <arg line="-all" /> -->
            <arg value="${idl.dir}/Demo.idl" />
        </java>
    </target>
    
    <!-- Compile your sources. -->
    <target name="compile" depends="idl-compile">
        <mkdir dir="${build.dir}"/>
	<javac
	    srcdir="${gen.dir}" destdir="${build.dir}"
            debug="on" deprecation="on" source="1.5" target="1.5"
            includeantruntime="true"
            classpathref="project.classpath">
<!--		<compilerarg line="-Xlint:all"/> -->
	</javac>
        <javac 
            srcdir="${src.dir}"	destdir="${build.dir}"
            debug="on" deprecation="on" source="1.5" target="1.5"
            includeantruntime="true"
            classpathref="project.classpath">
<!--		<compilerarg line="-Xlint:all"/>-->
	</javac>
    </target>
    
    <!-- Run the reserveration server. -->
    <target name="run-reservation-server" depends="compile">
        <java fork="true" 
              classname="YOUR.FlightReservationServer"
              classpathref="project.classpath">
            <sysproperty key="org.omg.CORBA.ORBClass" value="org.jacorb.orb.ORB"/>
            <sysproperty key="org.omg.CORBA.ORBSingletonClass" value="org.jacorb.orb.ORBSingleton"/>
            <sysproperty key="ORBInitRef.NameService" value="file://${ns.dir.location}/NS_REF"/>
        </java>
    </target>
    
    <!-- Run an airline server. -->
    <target name="run-airline-server" depends="compile">
        <java fork="true" 
              classname="AirlineServer.Server"
              classpathref="project.classpath">
            <sysproperty key="org.omg.CORBA.ORBClass" value="org.jacorb.orb.ORB"/>
            <sysproperty key="org.omg.CORBA.ORBSingletonClass" value="org.jacorb.orb.ORBSingleton"/>
            <sysproperty key="ORBInitRef.NameService" value="file://${ns.dir.location}/NS_REF"/>
        </java>
    </target>
    
    <!-- Test your solution. -->
    <target name="test" depends="compile">
        <junit printsummary="yes" haltonfailure="yes" fork="yes">
            <sysproperty key="org.omg.CORBA.ORBClass" value="org.jacorb.orb.ORB"/>
            <sysproperty key="org.omg.CORBA.ORBSingletonClass" value="org.jacorb.orb.ORBSingleton"/>
            <sysproperty key="ORBInitRef.NameService" value="file://${ns.dir.location}/NS_REF"/>
            <classpath refid="project.classpath"/>
            <formatter type="plain"/>
            <test name="test.TestAirlineServer"/>
            <test name="test.TestFlightReservationSystem"/>
        </junit>
    </target>
    
    <!-- Clean up the artifacts. -->
    <target name="clean">
        <delete dir="${build.dir}"/>
        <delete dir="${ns.dir}"/>
	<delete dir="${gen.dir}"/>
        <delete>
            <fileset dir=".">
                <include name="TEST*"/>
                <include name="_nsdb_*"/>
            </fileset>
        </delete>
    </target>
    
</project>

idl/Demo.idl[Bearbeiten | Quelltext bearbeiten]

Die IDL ist so simpel wie moeglich gehalten (einfach in idl/Demo.idl plazieren):

#include <CosTransactions.idl>

module Demo {
        interface Servant : /* any transaction-aware server needs to inherit these interfaces: */
                CosTransactions::Resource,
                CosTransactions::TransactionalObject
        {
                // this function requires no transaction and is really simple
                void simpleFunction();

                // this function uses an *im*plicit transaction
                void implicitTransaction();

                // this function uses an *ex*plicit transaction
                void explicitTransaction( in CosTransactions::Control control_ );
        };
};

Server[Bearbeiten | Quelltext bearbeiten]

Dieser Server (entspricht dem AirlineServer des dritten Beispiels) ist transaction-aware und unterstuetzt sowohl implizite als auch explizite transactionen.

server.sh[Bearbeiten | Quelltext bearbeiten]

Du kannst den client mit folgendem Skript starten:

#!/bin/bash

NS_DIR=ns
JAVA_PROPERTIES="-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB -Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton -DORBInitRef.NameService=file://$NS_DIR/NS_REF"
export CLASSPATH=$CLASSPATH:build:$JACORB_HOME/lib/jacorb.jar:$JACORB_HOME/lib/idl.jar:$JACORB_HOME/lib/logkit-1.2.jar:$JACORB_HOME/lib/avalon-framework-4.1.5.jar

java $JAVA_PROPERTIES Server.Server

src/Server/Server.java[Bearbeiten | Quelltext bearbeiten]

Das ist im wesentlichen "boiler-plate" der aus den tutorials kopiert werden kann:

package Server;

import java.util.Properties;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import org.omg.CORBA.*;
import org.omg.CORBA.ORBPackage.*;
import org.omg.CosTransactions.*;
import org.omg.CosNaming.*;
import org.omg.PortableServer.*;

public class Server {
	public static void main( String[] args ) {
		// create the mandatory Properties-object
		Properties props = new Properties();
		props.put("org.omg.PortableInterceptor.ORBInitializerClass.TSClientInit",
			"org.jacorb.transaction.TransactionInitializer");

		// corba part starts here:
		ORB orb = ORB.init( args, props );
		try {
			// POA-Stuff
			POA poa = POAHelper.narrow( orb.resolve_initial_references("RootPOA") );
			poa.the_POAManager().activate();

			// instantiate the server-object
			org.omg.CORBA.Object o = poa.servant_to_reference(
				new ServantImpl( orb, poa, props ) );

			// get Namingserverice and register server in it
			NamingContextExt nc =
				NamingContextExtHelper.narrow(orb.resolve_initial_references("NameService"));
			nc.bind( nc.to_name( "server" ), o );

			// finally:
			orb.run();
		} catch( Exception e ) {
			System.out.println("Exception: " + e.getMessage() );
			e.printStackTrace();
			System.exit(1);
		}
	}
}

src/Server/Servant.java[Bearbeiten | Quelltext bearbeiten]

Die Klasse ist funktionstuechtig. Es sei darauf hingewiesen, dass hier trotzdem eigentlich ein wichtiges Prinzip von Transaktionen verletzt wird: es koennen nicht mehrere Transaktionen auf einmal ausgefuehrt werden, eine zweite Transaktion wartet immer bis die Erste fertig ist (oder einen timeout hat). Mein Server bei der Abgabe konnte das allerdings auch nicht, und es hat nicht gestoert.

package Server;

import java.util.Properties;
import java.util.LinkedList;
import java.util.List;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import Demo.*;

import org.omg.CORBA.*;
import org.omg.CORBA.ORBPackage.*;
import org.omg.CosTransactions.*;
import org.omg.CosNaming.*;
import org.omg.PortableServer.*;

public class ServantImpl extends ServantPOA {
	private ORB orb;
	private Lock lock = new Lock();
	private Coordinator coord;

	List<Integer> data = new LinkedList<Integer>();
	List<Integer> uncommittedData = new LinkedList<Integer>();

	/**
	 * constructor 
	 */
	public ServantImpl( ORB orb, POA poa, Properties props ) {
		this.orb = orb;
	}


	/***************************************
	 * implementation of the idl-interface *
	 ***************************************/

	/**
	 * a function that doesn't need any transactions
	 */
	public void simpleFunction() {
		System.out.println("simpleFunction");
	}

	/**
	 * a function that "implements" an implicit transaction.
	 */
	public void implicitTransaction() {
		System.out.println("implicit transaction");
		// begin the transaction
		this.beginTransaction( this.getControl() );
		
		// put all new data (that may be passed on by the client as a
		// parameter) into the _uncommitted_Data:
		this.uncommittedData.add( new Integer( 2 ) );

		// note that after the end of this function, the permanent data
		// is still unchanged!
	}

	/**
	 * a function that "implements" an explicit transaction
	 */
	public void explicitTransaction( org.omg.CosTransactions.Control control ) {
		System.out.println("explicit transaction");
		// begin the transaction
		this.beginTransaction( control );

		// put all new data (that may be passed on by the client as a
		// parameter) into the _uncommitted_Data:
		this.uncommittedData.add( new Integer( 2 ) );
		
		// note that after the end of this function, the permanent data
		// is still unchanged!
	}


	/************************************************
	 * implementation of the transactions-functions *
	 ************************************************/

	/**
	 * forget - never used - use of this function is a mystery
	 */
	public void forget() {
	}

	/**
	 * implements the commit_once_phase operation
	 *
	 * This function is called when the client decides it is time to commit.
	 * Usually, It would be up to the prepare-function to decide if we can
	 * commit but this functionality is not required here, so we commit
	 * right away.
	 */
	public void commit_one_phase() {
		if (prepare() == Vote.VoteCommit) {
			commit();
		}
	}

	/**
	 * implements the commit operation. This function saves the yet
	 * uncommitted data to the "permanent" storage.
	 */
	public void commit() {
		System.out.println("Doing a commit.");
		// finally add new data
		this.data.addAll( this.uncommittedData );

		// clear uncommitted data
		this.uncommittedData = new LinkedList<Integer>();

		this.lock.unlock();
	}

	/**
	 * implements the rollback operation. Called whenever *anything* goes
	 * wrong.
	 */
	public void rollback() {
		// clear uncommitted data
		this.uncommittedData = new LinkedList<Integer>();

		this.lock.unlock();
	}

	/**
	 * implements the prepare operation
	 */
	public Vote prepare() {
		return Vote.VoteCommit;
	}

	/********************
	 * helper-functions *
	 ********************/

	/**
	 * get a control object for implicit transactions
	 * @return Control the Control object
	 */
	private Control getControl() {
		try {
			return org.omg.CosTransactions.CurrentHelper.narrow(
				orb.resolve_initial_references("TransactionCurrent")).get_control();
		} catch (Exception e) {
			e.printStackTrace();
			throw new org.omg.CORBA.TRANSACTION_ROLLEDBACK();
		}
	}

	/**
	 * begin a new transaction
	 * @param control the control-object of the current transaction
	 * @throws org.omg.CORBA.TRANSACTION_ROLLEDBACK if any error occurs
	 */
	private void beginTransaction( Control control ) {
		try {
			coord = control.get_coordinator();	
			if ( this.lock.lock( coord ) ) {
				// register resource
				coord.register_resource( _this() );
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new org.omg.CORBA.TRANSACTION_ROLLEDBACK();
		}
	}

}

src/Server/Lock.java[Bearbeiten | Quelltext bearbeiten]

Implementiert eine simple Lock-klasse. Der Coordinator bewirkt, dass die Klasse nicht blockiert wenn innerhalb einer Transaktion mehrmals versucht wird zu locken. Dies ist ein Hack um zu erreichen, dass implicitTransaction bzw. explicitTransaction innerhalb einer Transaktion mehrmals aufgerufen werden kann.

package Server;

import org.omg.CosTransactions.Coordinator;
import org.omg.CosTransactions.CoordinatorOperations;

public class Lock
{
	Coordinator coord = null;
	private boolean locked;

	// ctor
	public Lock()
	{
		this.locked = false;
	}

	/**
	 * lock this lock
	 * @return true if this is a new transaction
	 */
	public synchronized boolean lock( Coordinator newCoordinator )
	{
		if ( this.coord == null ) {
			this.locked = true;
			coord = newCoordinator;
			return true;
		}

		if ( this.coord.is_same_transaction( newCoordinator ) ) {
			// we are still in the same transaction
			if ( ! this.locked ) {
				System.out.println( "NO MORE CHEESE!" );
			}
			return false;
		}
			
		while( this.locked ) {
			try {
				wait();
			} catch ( InterruptedException e ) {
				System.out.println( "NO MORE CHEESE!" );
			}
		}
		locked = true;
		coord = newCoordinator;
		return true;
	}

	/**
	 * inlock the current lock
	 */
	public synchronized void unlock()
	{
		this.coord = null;
		this.locked = false;
		notifyAll();
	}
	
	/**
	 * ask if lock is locked
	 * @return boolean true if locked
	 */
	public synchronized boolean isLocked() {
		return locked;
	}
}

Client[Bearbeiten | Quelltext bearbeiten]

Der Client ist ein sehr, sehr simpler Client, der nichts grossartiges tut ausser die Methoden am server aufrufen, transaktionen beginnen und beenden. In der Angabe vom WS07 waere dieses Programm teil des FlightReservationSystems (der allerdings wieder selbst ein Server ist, der aber keine Transaktionen bieten muss)

client.sh[Bearbeiten | Quelltext bearbeiten]

Du kannst den client mit folgendem Skript starten:

#!/bin/bash

NS_DIR=ns
JAVA_PROPERTIES="-Dorg.omg.CORBA.ORBClass=org.jacorb.orb.ORB -Dorg.omg.CORBA.ORBSingletonClass=org.jacorb.orb.ORBSingleton -DORBInitRef.NameService=file://$NS_DIR/NS_REF"
export CLASSPATH=$CLASSPATH:build:$JACORB_HOME/lib/jacorb.jar:$JACORB_HOME/lib/idl.jar:$JACORB_HOME/lib/logkit-1.2.jar:$JACORB_HOME/lib/avalon-framework-4.1.5.jar

#echo $JAVA_PROPERTIES

java $JAVA_PROPERTIES Client.Client

src/Client/Client.java[Bearbeiten | Quelltext bearbeiten]

Wieder hauptsaechlich boiler-plate.

package Client;

import Server.*;
import Demo.*;

import java.util.Properties;

import org.omg.CORBA.*;
import org.omg.CORBA.ORBPackage.*;
import org.omg.CosTransactions.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.PortableServer.*;

public class Client {

	public static void main( String[] args ) {
		// create properties
		Properties props = new Properties();
		props.put("org.omg.PortableInterceptor.ORBInitializerClass.TSClientInit",
			"org.jacorb.transaction.TransactionInitializer");

		try {
			// get nameservice
			ORB orb = ORB.init( args, props );
			NamingContextExt nc =
				NamingContextExtHelper.narrow(orb.resolve_initial_references("NameService"));

			// get poa
			POA poa = POAHelper.narrow( orb.resolve_initial_references("RootPOA") );
			poa.the_POAManager().activate();

			// get server from nameservice:
			Demo.Servant server = ServantHelper.narrow(
				nc.resolve( nc.to_name( "server" ) ) );

			// finally, we are able to do something:
			ClientClass client = new ClientClass( server, orb );
			client.call_simpleFunction();
			client.call_implicitTransaction();
			client.call_explicitTransaction();
		} catch( Exception e ) {
			System.out.println("Exception: " + e.getMessage() );
			e.printStackTrace();
			System.exit(1);
		}
	}
}

src/Client/ClientClass.java[Bearbeiten | Quelltext bearbeiten]

Hier waere in einem vollstaendigen Client dann die eigentliche Programmlogik die mit dem Server interagiert versteckt.

package Client;

import Server.*;
import Demo.*;

import org.omg.PortableServer.*;
import org.omg.CORBA.*;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.CosTransactions.*;
import org.omg.CosTransactions.TransactionFactory;
import org.omg.CosTransactions.TransactionFactoryHelper;

public class ClientClass {
	ORB orb;
	private int timeout = 10;

	// the reference to the server:
	private Demo.Servant server;

	// for implicit transactions:
	private org.omg.CosTransactions.Current ts_current = null;
	// for explicit transactions:
	private Control control = null;
	private TransactionFactory transactionFactory = null;

	/** 
	 * simple constructor
	 */
	public ClientClass( Demo.Servant server, ORB orb ) {
		this.server = server;
		this.orb = orb;

		try {
			// for implicit transactions:
			this.ts_current =  org.omg.CosTransactions.CurrentHelper.narrow(
				orb.resolve_initial_references("TransactionCurrent"));

			// for explicit transactions:
			NamingContextExt nc = NamingContextExtHelper.narrow(
				orb.resolve_initial_references("NameService"));
			NameComponent[] name = new NameComponent[1];
			name[0] = new NameComponent("TransactionService", "service");
			transactionFactory = TransactionFactoryHelper.narrow( nc.resolve(name) );
		} catch ( Exception e ) {
			e.printStackTrace();
		}
	}


	/**
	 * simply pass on some functions to the server
	 */
	public void call_simpleFunction() {
		System.out.println("Calling a simple function");
		server.simpleFunction();
	}

	public void call_implicitTransaction() {
		System.out.println("make an implicit transaction");
		this.beginImplicitTransaction();
		
		/* Here we actually call the functions on the server(s).
		 * As soon as anything on any of the servers goes wrong, we
		 * have to do a rollback. Only after everything was successfull
		 * we can do a commit */
		try {
			server.implicitTransaction();
		} catch ( Exception e ) {
			this.rollbackExplicitTransaction();
		}

		this.commitImplicitTransaction();
	}
	
	public void call_explicitTransaction() {
		System.out.println("make an explicit transaction");
		this.beginExplicitTransaction();

		/* Here we actually call the functions on the server(s).
		 * As soon as anything on any of the servers goes wrong, we
		 * have to do a rollback. Only after everything was successfull
		 * we can do a commit. */
		try {
			server.explicitTransaction( this.control );
		} catch ( Exception e ) {
			this.rollbackExplicitTransaction();
		}

		this.commitExplicitTransaction();
	}

	/**
	 * begin an implicit transaction
	 */
	private void beginImplicitTransaction() {
		ts_current.set_timeout( timeout );
		try {
			ts_current.begin();
		} catch (SubtransactionsUnavailable e1) {
			e1.printStackTrace();
			System.exit(1);
		}
	}

	/**
	 * commit an implicit transaction
	 */
	private void commitImplicitTransaction() {
		try {
			ts_current.commit( true );
			System.out.println("Commit successfull");
		} catch (Exception e) {
			System.out.println( "Error: Commit failed: " + e.getMessage() );
			e.printStackTrace();
			try {
				System.out.println("making rollback.");
				ts_current.rollback();
				e.printStackTrace();
			} catch ( NoTransaction f ) {
				System.out.println( "Error: Rollback failed to - I want to die!" );
				f.printStackTrace();
				System.exit(1);
			}
		}
	}

	/**
	 * roll a (failed) implicit transaction back
	 */
	private void rollbackImplicitTransaction() {
		try {
			ts_current.rollback();
		} catch ( NoTransaction e ) {
			System.out.println("FATAL ERROR IN ROLLBACK!");
			e.printStackTrace();
			System.exit( 1 ); // make sure we spot this right away ;-)
		}
	}

	/**
	 * begin an explicit transaction
	 */
	private void beginExplicitTransaction() {
		this.control = transactionFactory.create( this.timeout );
	}

	/**
	 * commit an explicit transaction
	 */
	private void commitExplicitTransaction() {
		try {
			this.control.get_terminator().commit(true);
		} catch (Exception e) {
			System.out.println( "Error: Commit failed: " + e.getMessage() );
			e.printStackTrace();
			try {
				System.out.println("making rollback.");
				this.control.get_terminator().rollback();
				e.printStackTrace();
			} catch ( Unavailable f ) {
				System.out.println( "Error: Rollback failed to - I want to die!" );
				f.printStackTrace();
				System.exit(1);
			}
		}
	}

	/**
	 * roll a (failed) explicit transaction back
	 */
	private void rollbackExplicitTransaction() {
		try {
			this.control.get_terminator().rollback();
		} catch ( Unavailable e ) {
			System.out.println("FATAL ERROR IN ROLLBACK!");
			e.printStackTrace();
			System.exit( 1 ); // make sure we spot this right away ;-)
		}
	}
}