CS 696 Emerging Technologies: Java Distributed Computing Spring Semester, 1999 An Activatable Jini Service |
||
---|---|---|
© 1999, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 25-Apr-99 |
An Activatable Jini Service
This example will demonstrate a simple activatable Jini service. In other examples done in these notes, the goal has been to illustrate a Jini feature, not to implement a realistic server. In this example the goal was to illustrate a reasonable Jini server. The client code and the registration code still needs work. The HelloServer does not do much, so some issues are not demonstrated.
Activation
Making the server activatable helps insure that the server will be available for clients. The rmid daemon will keep the server active. Since rmid will restart the server process, this means that the server will have to register itself with Jini lookup service (reggie). There are two complicating issues that must be addressed.
First, If the server registers itself with the lookup service, the server must be activated to make this happen. The server is not activated until a client references the server. Clients use the lookup service to access the server! When you register the server with the activation system, you receive a stub object for the server. Use this stub to access the server, which will activate the server and cause it to be registered with the Jini lookup service. See the RegisterHelloServer class below for details.
Second, since the server is registered with the lookup service there is a lease to maintain. If the server is maintaining the lease, then the server must always be running. This can be done by setting the restart parameter in the java.rmi.activation.ActivationDesc class. See the RegisterHelloServer class below for details. (The Jini transaction manager appears to have a way around this issue.) If the server does not maintain its lease with the lookup service, then some other process must maintain the lease. The server must register with that process. If one has a number of lightly used Jini servers (services) you could let the servers be activated on demand. Use a persistent lease service to maintain the leases for these lightly-used servers.
Saving ServiceID
Each Jini service (or server) is assigned a unique service id the first time it registers with a Jini lookup service. This service id is to be used in all subsequent registrations with any Jini lookup service. The example shows one way of saving this service id and using it in later registrations. See the HelloServer class for details.
Delayed Registration
Bringing up a Jini aware machine/network after begin down could cause a packet storm when all the Jini services/servers join with the lookup services, which are also just coming up and joining other lookup services. This example has a random length delay between 0 and 15 seconds to mitigate this problem. See the HelloServer class for details.
Logging
If your server/client code is bug free, you never have network problems, no one ever tries to break your server/client system, your machines never crash or go down, or there is no need to know how often the system is used, then you do not need to using logging. The HelloServer uses the logging system available in the SDSU Java Library. Log results can be sent to a file or to the screen. There are different types of log messages. Debug statements can be turned off so they can be left in the code. The debug statements in HelloServer can be turned on/off by changing a value in a configuration file. The change only takes place after the server is restarted. The configuration file is in java.util.Propterties format. All key-value pairs are optional. Default values are type=file, debug=on, file= ServerLog {set in HelloServer.initializeLogging()}.The configuration file format is:
type=file | screen
debug=on | off
file=fileName
So a sample file is:
type=file
debug=off
The example assumes that the file is named "log.properties” located in path in the variable serverDataDir of the RegisterHelloServer program. See the RegisterHelloServer class for more details.
Admin Interface
I did not provide an administration interface for the service. It would be useful to turn debugging on and off dynamically via an administration interface. The current Jini Admin interfaces have no security.
The Example
HelloInterface
The standard HelloInterface.
import java.rmi.Remote; import java.rmi.RemoteException; public interface HelloInterface extends Remote { public String sayHello() throws RemoteException; }
HelloClient
Should use discovery here, but did not have time. This is the standard hello client.
import net.jini.core.entry.Entry; import net.jini.core.lookup.ServiceTemplate; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.discovery.LookupLocator; import net.jini.lookup.entry.Name; import java.rmi.RMISecurityManager; public class HelloClient { private static final REGGIE_URL = "jini://eli.sdsu.edu"; private static final SERVER_LABEL = "HelloServer"; public static void main (String[] args) throws Exception { System.setSecurityManager (new RMISecurityManager ()); LookupLocator lookup = new LookupLocator(REGGIE_URL); ServiceRegistrar registrar = lookup.getRegistrar (); Entry[] serverAttributes = new Entry[1]; serverAttributes[0] = new Name( SERVER_LABEL ); ServiceTemplate template = new ServiceTemplate (null, null, serverAttributes); HelloInterface myServerInterface = (HelloInterface) registrar.lookup (template); System.out.println ( myServerInterface.sayHello () ); } }
HelloServer
Click here for the source code.
public class HelloServer extends Activatable implements HelloInterface, ServiceIDListener { //MarshalledObject hash keys // In passing data to the HelloServer in the constructor public static final String GROUP_KEY = "reggieGroups"; public static final String DATA_DIR_KEY = "dataStore"; public static final String SERVICE_LABEL_KEY = "name"; // File used to store the service id. private static final String SERVICE_ID_FILE = "serviceID"; // Logging keys and default values private static final String LOG_SETTING_FILE = "log.properties"; private static final String LOG_FILE = "ServerLog"; private static final String LOG_FILE_KEY = "file"; private static final String LOG_TYPE_KEY = "type"; private static final String LOG_TYPE_FILE = "file"; private static final String LOG_TYPE_SCREEN = "screen"; private static final String DEBUG_KEY = "debug"; private static final String DEBUG_ON = "on"; // Directory containing log file, log configuration file and // service ID file, value comes from marshalledObject private File storageLocation; // the server is registered under this name with lookup service // value is obtained from marshalledObject passed to server private String serviceName;
Constructor
public HelloServer( ActivationID id, MarshalledObject data) throws RemoteException { super(id, 0); try { // Get the data from the MarshalledObject HashMap initialData = (HashMap) data.get(); String[] groups = (String[]) initialData.get( GROUP_KEY ); String dataLocation = (String )initialData.get( DATA_DIR_KEY ); serviceName = (String )initialData.get( SERVICE_LABEL_KEY ); storageLocation = new File( dataLocation ); initializeLogging(); Logger.log( "HelloServer Started" ); joinReggie( groups); } catch (Exception startupError) { Logger.error( "Start up error" ); Logger.error( startupError ); } }
HelloServer Public Methods
public void serviceIDNotify (ServiceID uniqueID) { Logger.log( "Obtained serviceID " + uniqueID ); saveServiceID( uniqueID ); } public String sayHello () throws RemoteException { try { Logger.log( "Connection from " + getClientHost() ); } catch (java.rmi.server.ServerNotActiveException error) { Logger.log( "Connection, client data not available" ); } return ("Hello World from Jini Hello server!"); }
HelloServer Joining a Lookup Service
private void joinReggie( String[] groups ) throws IOException { Logger.debug( "Join" ); ServiceID helloID = getServiceID(); Entry[] labels = getServiceLabels(); // Avoid all Jini services registering at same time when // machine is brought up - use random delay randomPause( 1000 * 15 ); if (helloID == null ) { Logger.debug( "Join, no ID" ); new JoinManager(this, labels, groups, null, this, new LeaseRenewalManager () ); } else { Logger.debug( "Join with ID" ); new JoinManager( helloID, this,labels, groups, null, new LeaseRenewalManager () ); } } private Entry[] getServiceLabels() { Entry[] labels = new Entry[1]; labels[0] = new Name(serviceName); return labels; }
RandomPause
/** * Delay execution for random time between 0 and maxDelay * milliseconds */ private void randomPause(int maxDelay) { try { Random delayGenerator = new Random(); long delay = delayGenerator.nextInt( maxDelay ); Logger.debug( "Pause for " + delay + " milliseconds" ); Thread.sleep( delay ); } catch (InterruptedException interuptedError ) {//just proceed, as this is just a pause method } }
Saving/Reading ServiceID
saveServiceID
/** * Save the service id in a file. If save does not succeed, no file * will be created. */ private void saveServiceID( ServiceID uniqueID ) { File serviceIDFile = new File( storageLocation, SERVICE_ID_FILE ); try { FileOutputStream out = new FileOutputStream( serviceIDFile ); BufferedOutputStream buffered = new BufferedOutputStream( out); DataOutputStream cout = new DataOutputStream( buffered); uniqueID.writeBytes( cout ); cout.close(); } catch (IOException writeProblem) { Logger.error( "Error in writing service id file" ); Logger.error( writeProblem ); serviceIDFile.delete(); } }
getServiceID
/** * Recover ServiceID from file. Return null if ServiceID * does not exist or can not be recovered */ private ServiceID getServiceID() { File serviceIDFile = new File( storageLocation, SERVICE_ID_FILE ); if ( !serviceIDFile.exists() ) return null; try { FileInputStream in = new FileInputStream( serviceIDFile ); BufferedInputStream buffered = new BufferedInputStream( in); DataInputStream cin = new DataInputStream( buffered); ServiceID id = new ServiceID( cin ); cin.close(); return id; } catch (IOException readProblem) { Logger.error( "Error in reading service id file" ); Logger.error( readProblem ); return null; } }
The logging System
private void initializeLogging( ) { Properties logSettings = getLogProperties( ); String logType = logSettings.getProperty(LOG_TYPE_KEY,LOG_TYPE_FILE ); String debug = logSettings.getProperty( DEBUG_KEY, DEBUG_ON ); String logFileName = logSettings.getProperty( LOG_FILE_KEY, LOG_FILE ); SelectiveLogger serverLogger; try { if ( logType.equals( LOG_TYPE_SCREEN ) ) serverLogger = (SelectiveLogger) ScreenLogger.register( ); else { File logFile = new File( storageLocation, logFileName ); serverLogger = (SelectiveLogger) FileLogger.register( logFile.getPath() ); } if (debug.equals(DEBUG_ON) ) serverLogger.debugOn(); else serverLogger.debugOff(); } catch (LoggerCreationException logFileProblem ) { // Can't create Filelogger so use screenlogger. ScreenLogger.register( ); } }
getLogProperties
private Properties getLogProperties() { Properties logSettings = new Properties(); File logSettingFile = new File( storageLocation, LOG_SETTING_FILE); try { if ( logSettingFile.exists() ) { FileInputStream in = new FileInputStream( logSettingFile ); BufferedInputStream buffered = new BufferedInputStream( in); logSettings.load( buffered ); } return logSettings; } catch (Exception readLoadError) { return new Properties(); } }
RegisterHelloServer
Click here for the source code
You must change the values of serverClassLocation, serverDataDir, policyFileLocation, and reggieGroups if you wish to run this example. ServerClassLocation is the path to your copy of the HelloServer class. ServerDataDir is directory you wish the the HelloServer to place its log files, store the service id , and where the log.properties file is placed.
public class RegisterHelloServer { // Place important constants in one place static String serviceLabel = "HelloServer"; static String serverClass = "HelloServer"; static String serverClassLocation = "file:/export/home/whitney/java/whitney/jini/examples/activatable/"; static String serverDataDir = "/export/home/whitney/java/whitney/jini/examples/activatable/ServerData"; static String policyFileLocation = "/export/home/whitney/jini_data"; static String[] reggieGroups = { "whitney" }; public static void main( String[] args ) throws Exception { System.setSecurityManager(new RMISecurityManager()); System.out.println("Start main"); Properties policyFileLocation = new Properties(); policyFileLocation.put("policy", policyFileLocation); ActivationGroupDesc.CommandEnvironment ace = null; ActivationGroupDesc exampleGroup = new ActivationGroupDesc(policyFileLocation, ace); ActivationGroupID agi = ActivationGroup.getSystem().registerGroup(exampleGroup); ActivationGroup.createGroup(agi, exampleGroup, 0);
RegisterHelloServer Continued
HashMap initializationData = new HashMap(); initializationData.put( HelloServer.GROUP_KEY, reggieGroups ); initializationData.put( HelloServer.DATA_DIR_KEY, serverDataDir ); initializationData.put( HelloServer.SERVICE_LABEL_KEY, serviceLabel ); MarshalledObject data = new MarshalledObject( initializationData ); ActivationDesc desc = new ActivationDesc(serverClass, serverClassLocation, data, true); System.out.println("Register with rmid"); HelloInterface server = (HelloInterface) Activatable.register(desc); System.out.println("Class registered with rmid"); //Force object to be activated by accessing it. System.out.println( server.sayHello() ); System.out.println("Class activated"); } }
Copyright ©, All rights reserved.
1999 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.
Previous    visitors since 25-Apr-99    Next