From: Bryan T. <tho...@us...> - 2007-03-22 21:11:32
|
Update of /cvsroot/cweb/bigdata/src/java/com/bigdata/service In directory sc8-pr-cvs4.sourceforge.net:/tmp/cvs-serv3112/src/java/com/bigdata/service Modified Files: EmbeddedDataService.java package.html DataService.java IDataService.java DataServiceClient.java Added Files: AbstractServer.java DataServer.java IRemoteTxCommitProtocol.java Removed Files: AbstractService.java Log Message: Working on service startup and signal handling. --- NEW FILE: DataServer.java --- /** The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product. Contributors to any Modifications may add their own copyright notices to identify their own contributions. License: The contents of this file are subject to the CognitiveWeb Open Source License Version 1.1 (the License). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License from http://www.CognitiveWeb.org/legal/license/ Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyrights: Portions created by or assigned to CognitiveWeb are Copyright (c) 2003-2003 CognitiveWeb. All Rights Reserved. Contact information for CognitiveWeb is available at http://www.CognitiveWeb.org Portions Copyright (c) 2002-2003 Bryan Thompson. Acknowledgements: Special thanks to the developers of the Jabber Open Source License 1.0 (JOSL), from which this License was derived. This License contains terms that differ from JOSL. Special thanks to the CognitiveWeb Open Source Contributors for their suggestions and support of the Cognitive Web. Modifications: */ /* * Created on Mar 22, 2007 */ package com.bigdata.service; import java.rmi.Remote; import java.util.Properties; /** * The bigdata data server. * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> * @version $Id$ */ public class DataServer extends AbstractServer { /** * @param args */ public DataServer(String[] args) { super(args); } public static void main(String[] args) { new DataServer(args).run(); } protected Remote newService(Properties properties) { return new DataService(properties); } } Index: DataService.java =================================================================== RCS file: /cvsroot/cweb/bigdata/src/java/com/bigdata/service/DataService.java,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** DataService.java 22 Mar 2007 15:04:15 -0000 1.4 --- DataService.java 22 Mar 2007 21:11:23 -0000 1.5 *************** *** 48,52 **** --- 48,54 ---- package com.bigdata.service; + import java.io.IOException; import java.net.InetSocketAddress; + import java.rmi.Remote; import java.util.Properties; import java.util.concurrent.Callable; *************** *** 93,96 **** --- 95,100 ---- * @version $Id$ * + * @see DataServer, which is used to start this service. + * * @see NIODataService, which contains some old code that can be refactored for * an NIO interface to the data service. *************** *** 139,144 **** * {read,write,create,delete} access to a temporary directory and a data * directory. */ ! public class DataService extends AbstractService implements IDataService, IWritePipeline, IResourceTransfer { --- 143,152 ---- * {read,write,create,delete} access to a temporary directory and a data * directory. + * + * @todo all of the interfaces implemented by this class need to extend + * {@link Remote} in order to be made visible on the proxy object + * exported by JERI. */ ! public class DataService implements IDataService, IWritePipeline, IResourceTransfer { *************** *** 292,296 **** */ ! public long commit(long tx) { // will place task on writeService and block iff necessary. --- 300,304 ---- */ ! public long commit(long tx) throws IOException { // will place task on writeService and block iff necessary. *************** *** 299,303 **** } ! public void abort(long tx) { // will place task on writeService iff read-write tx. --- 307,311 ---- } ! public void abort(long tx) throws IOException { // will place task on writeService iff read-write tx. Index: IDataService.java =================================================================== RCS file: /cvsroot/cweb/bigdata/src/java/com/bigdata/service/IDataService.java,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** IDataService.java 17 Mar 2007 23:14:58 -0000 1.2 --- IDataService.java 22 Mar 2007 21:11:24 -0000 1.3 *************** *** 48,51 **** --- 48,52 ---- package com.bigdata.service; + import java.io.IOException; import java.util.concurrent.ExecutionException; *************** *** 82,86 **** * @version $Id$ */ ! public interface IDataService extends ITxCommitProtocol { /** --- 83,87 ---- * @version $Id$ */ ! public interface IDataService extends IRemoteTxCommitProtocol { /** *************** *** 128,132 **** */ public void batchOp(long tx, String name, IBatchOp op) ! throws InterruptedException, ExecutionException; /** --- 129,133 ---- */ public void batchOp(long tx, String name, IBatchOp op) ! throws InterruptedException, ExecutionException, IOException; /** *************** *** 160,164 **** */ public void submit(long tx, IProcedure proc) throws InterruptedException, ! ExecutionException; /** --- 161,165 ---- */ public void submit(long tx, IProcedure proc) throws InterruptedException, ! ExecutionException, IOException; /** *************** *** 189,193 **** */ public RangeQueryResult rangeQuery(long tx, String name, byte[] fromKey, ! byte[] toKey, int flags) throws InterruptedException, ExecutionException; // /** --- 190,195 ---- */ public RangeQueryResult rangeQuery(long tx, String name, byte[] fromKey, ! byte[] toKey, int flags) throws InterruptedException, ! ExecutionException, IOException, IOException; // /** --- NEW FILE: IRemoteTxCommitProtocol.java --- /** The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product. Contributors to any Modifications may add their own copyright notices to identify their own contributions. License: The contents of this file are subject to the CognitiveWeb Open Source License Version 1.1 (the License). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License from http://www.CognitiveWeb.org/legal/license/ Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyrights: Portions created by or assigned to CognitiveWeb are Copyright (c) 2003-2003 CognitiveWeb. All Rights Reserved. Contact information for CognitiveWeb is available at http://www.CognitiveWeb.org Portions Copyright (c) 2002-2003 Bryan Thompson. Acknowledgements: Special thanks to the developers of the Jabber Open Source License 1.0 (JOSL), from which this License was derived. This License contains terms that differ from JOSL. Special thanks to the CognitiveWeb Open Source Contributors for their suggestions and support of the Cognitive Web. Modifications: */ /* * Created on Mar 22, 2007 */ package com.bigdata.service; import java.io.IOException; import java.rmi.Remote; import java.rmi.RemoteException; import com.bigdata.journal.AbstractJournal; import com.bigdata.journal.ITxCommitProtocol; import com.bigdata.journal.ValidationError; /** * Remote interface. * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> * @version $Id$ * * @todo reconcile API with {@link ITxCommitProtocol} which is declared by * {@link AbstractJournal}. I do not want to have IOException on all of * the bigdata interfaces, but you need either that or * {@link RemoteException} for an interface that will be exposed by a jini * service (unless you use a smart proxy?). */ public interface IRemoteTxCommitProtocol extends Remote { /** * Request commit of the transaction write set. */ public long commit(long tx) throws ValidationError, IOException; /** * Request abort of the transaction write set. */ public void abort(long tx) throws IOException; } Index: DataServiceClient.java =================================================================== RCS file: /cvsroot/cweb/bigdata/src/java/com/bigdata/service/DataServiceClient.java,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** DataServiceClient.java 17 Mar 2007 23:14:58 -0000 1.2 --- DataServiceClient.java 22 Mar 2007 21:11:24 -0000 1.3 *************** *** 48,54 **** --- 48,56 ---- package com.bigdata.service; + import java.io.IOException; import java.util.Properties; import java.util.concurrent.ExecutionException; + import com.bigdata.journal.ValidationError; import com.bigdata.objndx.IBatchOp; import com.bigdata.service.DataService.RangeQueryResult; *************** *** 112,116 **** } ! public void batchOp(long tx, String name, IBatchOp op) throws InterruptedException, ExecutionException { delegate.batchOp(tx, name, op); } --- 114,118 ---- } ! public void batchOp(long tx, String name, IBatchOp op) throws InterruptedException, ExecutionException, IOException { delegate.batchOp(tx, name, op); } *************** *** 120,136 **** // } ! public RangeQueryResult rangeQuery(long tx, String name, byte[] fromKey, byte[] toKey, int flags) throws InterruptedException, ExecutionException { return delegate.rangeQuery(tx, name, fromKey, toKey, flags); } ! public void submit(long tx, IProcedure proc) throws InterruptedException, ExecutionException { delegate.submit(tx, proc); } ! public void abort(long tx) { delegate.abort(tx); } ! public long commit(long tx) { return delegate.commit(tx); } --- 122,138 ---- // } ! public RangeQueryResult rangeQuery(long tx, String name, byte[] fromKey, byte[] toKey, int flags) throws InterruptedException, ExecutionException, IOException { return delegate.rangeQuery(tx, name, fromKey, toKey, flags); } ! public void submit(long tx, IProcedure proc) throws InterruptedException, ExecutionException, IOException { delegate.submit(tx, proc); } ! public void abort(long tx) throws IOException { delegate.abort(tx); } ! public long commit(long tx) throws ValidationError, IOException { return delegate.commit(tx); } Index: package.html =================================================================== RCS file: /cvsroot/cweb/bigdata/src/java/com/bigdata/service/package.html,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** package.html 22 Mar 2007 15:04:15 -0000 1.2 --- package.html 22 Mar 2007 21:11:23 -0000 1.3 *************** *** 140,143 **** --- 140,191 ---- </p> + <h2>unique identifiers</h2> + + <dl> + + <dt>Data Service</dt><dd><p>The serviceID identifies the data + service. If the service dies and then restarts on the same host with + the same data, then it is still the same service instance. If the + service dies and is recreated on another host with the same data, then + it is still the same instance. What is important is that (a) the + service has the same data (journals, index segments, and serviceID); + and (b) that the data for that service has not become <em>stale</em> + (or inconsistent).</p><p> The easiest way in which the data can become + stale is for the service to fail. When failover is operating, clients + will be redirected to one or more other services having consistent + data for the same index partitions. If clients then write on _any_ of + those index partitions and commit, then the old service now has stale + (aka inconsistent) data. A service with inconsistent data MUST NOT be + used. + </p><p>In general, the majority of state for a service will live in + its index segments. This can be hundreds or thousands of times more + data in the index segments than there is in any single journal. This + can make it worth while to make the service consistent again. + </p></dd> + + <dt>Journal</dt><dd>Each journal has a UUID that is generated when the + journal is created (it is stored in the root block). This serves to + uniquely identify the journal as a resource regardless of where it may + be located on the host or in the file system.</dd> + + <dt>Index Segment</dt><dd>Each index segment has a UUID that is + generated when the index segment is created (it is stored in the fixed + length index segment metadata record). This serves to uniquely + identify the index segment as a resource regardless of where it may be + located on the host or in the file system.</dd> + + <dt>Index</dt><dd>Named indices are assigned UUIDs when they are + created. This makes it possible to rename an index, since its UUID + remains the same.</dd> + + <dt>metadata service</dt><dd></dd> + + <dt>transaction manager service</dt><dd></dd> + + <dt>job scheduler service</dt><dd></dd> + + </dl> + + <h2>downloaded code</h2> Index: EmbeddedDataService.java =================================================================== RCS file: /cvsroot/cweb/bigdata/src/java/com/bigdata/service/EmbeddedDataService.java,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** EmbeddedDataService.java 17 Mar 2007 23:14:58 -0000 1.2 --- EmbeddedDataService.java 22 Mar 2007 21:11:23 -0000 1.3 *************** *** 48,51 **** --- 48,52 ---- package com.bigdata.service; + import java.io.IOException; import java.util.Properties; import java.util.concurrent.ExecutionException; *************** *** 53,56 **** --- 54,58 ---- import java.util.concurrent.Executors; + import com.bigdata.journal.ValidationError; import com.bigdata.objndx.IBatchOp; import com.bigdata.service.DataService.RangeQueryResult; *************** *** 124,132 **** } ! public void abort(long tx) { delegate.abort(tx); } ! public long commit(long tx) { return delegate.commit(tx); } --- 126,134 ---- } ! public void abort(long tx) throws IOException { delegate.abort(tx); } ! public long commit(long tx) throws ValidationError, IOException { return delegate.commit(tx); } --- NEW FILE: AbstractServer.java --- /** The Notice below must appear in each file of the Source Code of any copy you distribute of the Licensed Product. Contributors to any Modifications may add their own copyright notices to identify their own contributions. License: The contents of this file are subject to the CognitiveWeb Open Source License Version 1.1 (the License). You may not copy or use this file, in either source code or executable form, except in compliance with the License. You may obtain a copy of the License from http://www.CognitiveWeb.org/legal/license/ Software distributed under the License is distributed on an AS IS basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyrights: Portions created by or assigned to CognitiveWeb are Copyright (c) 2003-2003 CognitiveWeb. All Rights Reserved. Contact information for CognitiveWeb is available at http://www.CognitiveWeb.org Portions Copyright (c) 2002-2003 Bryan Thompson. Acknowledgements: Special thanks to the developers of the Jabber Open Source License 1.0 (JOSL), from which this License was derived. This License contains terms that differ from JOSL. Special thanks to the CognitiveWeb Open Source Contributors for their suggestions and support of the Cognitive Web. Modifications: */ /* * Created on Mar 18, 2007 */ package com.bigdata.service; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.rmi.Remote; import java.rmi.server.ExportException; import java.util.Properties; import net.jini.admin.JoinAdmin; import net.jini.config.Configuration; import net.jini.config.ConfigurationException; import net.jini.config.ConfigurationProvider; import net.jini.core.discovery.LookupLocator; import net.jini.core.entry.Entry; import net.jini.core.lookup.ServiceID; import net.jini.discovery.DiscoveryManagement; import net.jini.discovery.LookupDiscovery; import net.jini.discovery.LookupDiscoveryManager; import net.jini.export.Exporter; import net.jini.lease.LeaseListener; import net.jini.lease.LeaseRenewalEvent; import net.jini.lease.LeaseRenewalManager; import net.jini.lookup.JoinManager; import net.jini.lookup.ServiceIDListener; import org.apache.log4j.Logger; import sun.misc.Signal; import sun.misc.SignalHandler; /** * Abstract base class for configurable services discoverable using JINI. * Services are started using a <code>main</code> routine: * <pre> public static void main(String[] args) { new MyServer(args).run(); } * </pre> * * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> * @version $Id$ */ abstract public class AbstractServer implements LeaseListener, ServiceIDListener { public static final transient Logger log = Logger .getLogger(AbstractServer.class); private ServiceID serviceID; private DiscoveryManagement discoveryManager; private JoinManager joinManager; private Configuration config; /** * The file where the {@link ServiceID} will be written/read. */ private File serviceIdFile; /** * Responsible for exporting a proxy for the service. */ private Exporter exporter; /** * The service implementation object. */ private Remote impl; /** * The exported proxy for the service implementation object. */ private Remote proxy; /** * Server startup reads {@link Configuration} data from the file(s) named by * <i>args</i>, starts the service, and advertises the service for * discovery. Aside from the server class to start, the behavior is more or * less entirely parameterized by the {@link Configuration}. * * @param args * The command line arguments. */ protected AbstractServer(String[] args) { final String SERVICE_LABEL = "ServiceDescription"; final String ADVERT_LABEL = "AdvertDescription"; Entry[] entries = null; LookupLocator[] unicastLocators = null; String[] groups = null; try { config = ConfigurationProvider.getInstance(args); /* * Extract how the service will perform service discovery. */ groups = (String[]) config.getEntry(ADVERT_LABEL, "groups", String[].class, LookupDiscovery.ALL_GROUPS/* default */); unicastLocators = (LookupLocator[]) config.getEntry( ADVERT_LABEL, "unicastLocators", LookupLocator[].class, null/* default */); /* * Extract how the service will advertise itself from the * Configuration. */ entries = (Entry[]) config.getEntry(ADVERT_LABEL, "entries", Entry[].class, null/* default */); serviceIdFile = (File) config.getEntry(ADVERT_LABEL, "serviceIdFile", File.class); // default if(serviceIdFile.exists()) { try { serviceID = readServiceId(serviceIdFile); } catch(IOException ex) { log.fatal("Could not read serviceID from existing file: " + serviceIdFile); System.exit(1); } } else { log.info("New service instance - ServiceID will be assigned"); } /* * Extract how the service will provision itself from the * Configuration. */ // use the configuration to construct an exporter exporter = (Exporter) config.getEntry(// SERVICE_LABEL, // component "exporter", // name Exporter.class // type (of the return object) ); /* * Access the properties file used to configure the service. */ File propertyFile = (File) config.getEntry(SERVICE_LABEL, "propertyFile", File.class); Properties properties = new Properties(); try { InputStream is = new BufferedInputStream(new FileInputStream( propertyFile)); properties.load(is); is.close(); } catch (IOException ex) { log.fatal("Configuration error: "+ex, ex); System.exit(1); } // create the service object. impl = newService(properties); // export a proxy object for this service instance. proxy = exporter.export(impl); log.info("Proxy is " + proxy + "(" + proxy.getClass() + ")"); } catch(ConfigurationException ex) { log.fatal("Configuration error: "+ex, ex); System.exit(1); } catch (ExportException ex) { log.fatal("Export error: "+ex, ex); System.exit(1); } try { /* * Note: This class will perform multicast discovery if ALL_GROUPS * is specified and otherwise requires you to specify one or more * unicast locators (URIs of hosts running discovery services). As * an alternative, you can use LookupDiscovery, which always does * multicast discovery. */ discoveryManager = new LookupDiscoveryManager( groups, unicastLocators, null // DiscoveryListener ); // DiscoveryManagement discoveryManager = new LookupDiscovery( // groups); if (serviceID != null) { /* * We read the serviceID from local storage. */ joinManager = new JoinManager(proxy, // service proxy entries, // attr sets serviceID, // ServiceIDListener discoveryManager, // DiscoveryManager new LeaseRenewalManager()); } else { /* * We are requesting a serviceID from the registrar. */ joinManager = new JoinManager(proxy, // service proxy entries, // attr sets this, // ServiceIDListener discoveryManager, // DiscoveryManager new LeaseRenewalManager()); } } catch (IOException ex) { log.fatal("Lookup service discovery error: "+ex, ex); try { /* unexport the proxy */ unexport(true); joinManager.terminate(); discoveryManager.terminate(); } catch (Throwable t) { /* ignore */ } System.exit(1); } } /** * Unexports the proxy. * * @param force * When true, the object is unexported even if there are pending * or in progress service requests. * * @return true iff the object is (or was) unexported. * * @see Exporter#unexport(boolean) */ public boolean unexport(boolean force) { if(exporter.unexport(true)) { proxy = null; return true; } return false; } /** * Read and return the {@link ServiceID} from an existing local file. * * @param file * The file whose contents are the serialized {@link ServiceID}. * * @return The {@link ServiceID} read from that file. * * @exception IOException * if the {@link ServiceID} could not be read from the file. */ public ServiceID readServiceId(File file) throws IOException { FileInputStream is = new FileInputStream(file); ServiceID serviceID = new ServiceID(new DataInputStream(is)); is.close(); log.info("Read ServiceID=" + serviceID+" from "+file); return serviceID; } /** * This method is responsible for saving the {@link ServiceID} on stable * storage when it is invoked. It will be invoked iff the {@link ServiceID} * was not defined and one was therefore assigned. * * @param serviceID * The assigned {@link ServiceID}. */ public void serviceIDNotify(ServiceID serviceID) { log.info("serviceID=" + serviceID); if (serviceIdFile != null) { try { DataOutputStream dout = new DataOutputStream( new FileOutputStream(serviceIdFile)); serviceID.writeBytes(dout); dout.flush(); dout.close(); log.info("ServiceID saved: " + serviceIdFile); } catch (Exception ex) { log.error("Could not save ServiceID", ex); } } } /** * Note: This is only invoked if the automatic lease renewal by the lease * manager is denied by the service registrar. * * @todo how should we handle being denied a lease? Wait a bit and try * re-registration? There can be multiple discovery services and this * is only one lease rejection, so perhaps the service is still under * lease on another discovery service? */ public void notify(LeaseRenewalEvent event) { log.error("Lease could not be renewed: " + event); } /** * Run the server (this should be invoked from <code>main</code>. * * FIXME work through the install a signal handler that will shutdown the * service politely when it is invoked. Do we need -Xrs on the command * line for this to work? Which signals should be trapped? Does this * vary by OS? * * SIGINT Interactive attention (CTRL-C). JVM will exit normally. Yes <br> * SIGTERM Termination request. JVM will exit normally. Yes <br> * SIGHUP Hang up. JVM will exit normally. Yes * * @see http://www-128.ibm.com/developerworks/java/library/i-signalhandling/ */ protected void run() { log.info("Started server."); /* * Install signal handlers. */ ServerShutdownSignalHandler.install("SIGINT",this); // ServerShutdownSignalHandler.install("SIGTERM",this); /* * Wait until the server is terminated. */ Object keepAlive = new Object(); synchronized (keepAlive) { try { keepAlive.wait(); } catch (InterruptedException ex) { log.info(""+ex); } } } /** * Shutdown the server taking time only to unregister it from jini. * * @todo make this extensible? provide for normal shutdown vs this? support * the jini Admin interface. */ private void shutdownNow() { /* * Terminate manager threads. */ try { log.info("Terminating manager threads."); joinManager.terminate(); discoveryManager.terminate(); } catch (Exception ex) { log.error("Could not terminate: "+ex, ex); } /* * Unexport the proxy, making the service no longer available. If you do * not do this then the client can still make requests even after you * have terminated the join manager and the service is no longer visible * in the service browser. */ log.info("Unexporting the service proxy."); unexport(true); } /** * Signal handler shuts down the server politely. * * @author <a href="mailto:tho...@us...">Bryan Thompson</a> * @version $Id$ */ static class ServerShutdownSignalHandler implements SignalHandler { private final AbstractServer server; private SignalHandler oldHandler; protected ServerShutdownSignalHandler(AbstractServer server) { if(server == null) throw new IllegalArgumentException(); this.server = server; } /** * Install the signal handler. */ public static SignalHandler install(String signalName, AbstractServer server) { Signal signal = new Signal(signalName); ServerShutdownSignalHandler newHandler = new ServerShutdownSignalHandler( server); newHandler.oldHandler = Signal.handle(signal, newHandler); log.info("Installed handler: " + signal + ", oldHandler=" + newHandler.oldHandler); return newHandler; } public void handle(Signal sig) { log.warn("Signal: "+sig); /* * Handle signal. */ server.shutdownNow(); try { // Chain back to previous handler, if one exists if ( oldHandler != SIG_DFL && oldHandler != SIG_IGN ) { oldHandler.handle(sig); } } catch (Exception ex) { log.fatal("Signal handler failed, reason "+ex); System.exit(1); } } } /** * This method is responsible for creating the remote service implementation * object. This object MUST declare one or more interfaces that extent the * {@link Remote} interface. The server will use JERI to create a proxy for * the remote object and configure and manage the protocol for * communications between the client (service proxy) and the remote object * (the service implementation). * <p> * Note: You have to implement {@link JoinAdmin} in order to show up as an * administerable service (blue folder) in the jini Service Browser. * * @param properties * The contents of the {@link Properties} file whose name was * given by the <code>propertyFile</code> value in the * {@link Configuration} identified to <code>main</code> by its * command line arguments. */ abstract protected Remote newService(Properties properties); // /** // * The remote service implementation object. This implements the // * {@link Remote} interface and uses JERI to create a proxy for the remote // * object and configure and manage the protocol for communications between // * the client (service proxy) and the remote object (the service // * implementation). // * <p> // * Note: You have to implement {@link JoinAdmin} in order to show up as an // * administerable service (blue folder) in the jini Service Browser. // * // * @version $Id$ // * @author <a href="mailto:tho...@us...">Bryan Thompson // * </a> // */ // public static class TestServiceImpl implements ITestService // { // // /** // * Service constructor. // * // * @param properties // */ // public TestServiceImpl(Properties properties) { // // log.info("Created: " + this ); // // new Journal(properties); // // } // // public void invoke() { // // log.info("invoked: "+this); // // } // // } } --- AbstractService.java DELETED --- |