Mercurial > hg > blitz_condensed
diff src/org/dancres/blitz/remote/BlitzServiceImpl.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/dancres/blitz/remote/BlitzServiceImpl.java Sat Mar 21 11:00:06 2009 +0000 @@ -0,0 +1,1326 @@ +package org.dancres.blitz.remote; + +import java.io.IOException; + +import java.rmi.RemoteException; +import java.rmi.MarshalledObject; +import java.rmi.RMISecurityManager; +import java.rmi.NoSuchObjectException; + +import java.rmi.activation.ActivationID; +import java.rmi.activation.ActivationException; +import java.rmi.activation.ActivationSystem; +import java.rmi.activation.Activatable; + +import java.util.ArrayList; +import java.util.List; + +import java.util.logging.Logger; +import java.util.logging.Level; + +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import java.net.InetSocketAddress; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import net.jini.security.ProxyPreparer; +import net.jini.security.TrustVerifier; + +import net.jini.security.proxytrust.ServerProxyTrust; + +import net.jini.lookup.JoinManager; + +import net.jini.config.ConfigurationException; +import net.jini.config.Configuration; + +import net.jini.export.ProxyAccessor; +import net.jini.export.Exporter; + +import net.jini.activation.ActivationExporter; +import net.jini.activation.ActivationGroup; + +import net.jini.jeri.BasicILFactory; +import net.jini.jeri.BasicJeriExporter; + +import net.jini.jeri.tcp.TcpServerEndpoint; + +import net.jini.core.transaction.Transaction; +import net.jini.core.transaction.UnknownTransactionException; +import net.jini.core.transaction.TransactionException; + +import net.jini.core.transaction.server.TransactionManager; + +import net.jini.core.event.RemoteEventListener; +import net.jini.core.event.EventRegistration; + +import net.jini.core.lease.Lease; +import net.jini.core.lease.UnknownLeaseException; +import net.jini.core.lease.LeaseDeniedException; + +import net.jini.core.discovery.LookupLocator; + +import net.jini.core.entry.Entry; + +import net.jini.discovery.DiscoveryLocatorManagement; +import net.jini.discovery.DiscoveryGroupManagement; +import net.jini.discovery.DiscoveryManagement; + +import net.jini.space.JavaSpace; + +import com.sun.jini.start.LifeCycle; + +import org.dancres.blitz.mangler.MangledEntry; + +import org.dancres.blitz.lease.SpaceUID; +import org.dancres.blitz.lease.LeaseBounds; + +import org.dancres.blitz.config.ConfigurationFactory; + +import org.dancres.blitz.notify.RemoteEventDispatcher; + +import org.dancres.blitz.Logging; +import org.dancres.blitz.SpaceImpl; +import org.dancres.blitz.RegTicket; +import org.dancres.blitz.WriteTicket; +import org.dancres.blitz.EntryChit; +import org.dancres.blitz.EntryView; + +import org.dancres.blitz.txn.StoragePersonalityFactory; +import org.dancres.blitz.txn.StoragePersonality; + +import org.dancres.blitz.stats.Stat; +import org.dancres.blitz.stats.Switch; +import org.dancres.blitz.stats.StatsBoard; +import org.dancres.blitz.stats.SwitchSettings; + +import org.dancres.blitz.remote.view.EntryViewFactory; +import org.dancres.blitz.remote.view.ViewRegistration; +import org.dancres.blitz.remote.view.EntryViewUID; +import org.dancres.blitz.remote.txn.TransactionManagerImpl; +import org.dancres.blitz.remote.user.ColocatedAgent; + +/** + <p> The remote layer implementation for Blitz supporting both transient and + activatable modes of operation. Needs to implement ProxyAccessor to + provide a means of accessing it's core remote ref for the purposes of + converting this remote service implementation to a stub at appropriate + moments in the activation process </p>. + + <p> We need to publish a proxy for the Javaspace itself. If we implement + Administrable, this proxy should call back to the server's getAdmin() to + recovery an admin proxy which contains a remote stub to the admin methods. + The final proxy, for transactionparticipant can actually be a naked ref, + not even a proxy but this would allow immediate access to the other + interfaces of the stub which could be bad. </p> + + <p> JoinAdmin should be implemented by the admin proxy and the server + back end. Storage/manipulation of the JoinManager should be delegated to + a descendent of the JINIExporter LookupStorage code which should store + it's data in a Blitz meta registry. This means no configuration required + and keeps all the meta data in one neat place.</p> + */ +public class BlitzServiceImpl implements ServerProxyTrust, + ProxyAccessor, BlitzServer { + + static final Logger theLogger = + Logging.newLogger("org.dancres.blitz.remote", Level.INFO); + + private static BlitzServiceImpl theServiceImpl; + + private ActivationID theActivationID; + private ActivationSystem theActivationSystem; + + private LookupStorage theLookupStore; + + private Exporter theExporter; + + private BlitzServer theStub; + + private BlitzProxy theSpaceProxy; + + private JoinManager theJoinManager; + + private TxnGatewayImpl theTxnGate; + + private SpaceImpl theSpace; + + private AdminProxy theAdminProxy; + + private TxnParticipantProxy theTxnProxy; + + private LifeCycle theLifecycle; + + private boolean isStopped = false; + + private LoginContext theLoginContext; + + private boolean doCompatDestroy = false; + + private TransactionManagerImpl theLoopbackTxnMgr; + + /** + This constructor is required to use + com.sun.jini.start.NonActivatableServiceDescriptor. + */ + public BlitzServiceImpl(String [] anArgs, LifeCycle aLifeCycle) + throws RemoteException, ConfigurationException { + + try { + theLifecycle = aLifeCycle; + init(anArgs, true); + } catch (ConfigurationException aCE) { + destroyImpl(false); + throw aCE; + } catch (RemoteException anRE) { + destroyImpl(false); + throw anRE; + } + + /* + Keep a static reference to ourselves in order to avoid premature + GC + */ + theServiceImpl = this; + } + + /** + This constructor is required to use + com.sun.jini.start.SharedActivatableServiceDescriptor which invokes + on ActivateWrapper which will call this constructor. + SharedActivatabkeServiceDescriptor's serverConfigArgs are wrapped + into a MarshalledObject and pass in with the ActivationID. + */ + public BlitzServiceImpl(ActivationID anActivationID, + MarshalledObject aData) + throws RemoteException, ConfigurationException { + + theActivationID = anActivationID; + + String[] myArgs = null; + + try { + myArgs = (String[]) aData.get(); + } catch (Exception anE) { + theLogger.log(Level.SEVERE, "Failed to unpacked marshalled args", + anE); + throw new RemoteException("Failed to unpack marshalled args"); + } + + try { + init(myArgs, true); + } catch (ConfigurationException aCE) { + destroyImpl(false); + throw aCE; + } catch (RemoteException anRE) { + destroyImpl(false); + throw anRE; + } + + theServiceImpl = this; + } + + /** + * Use this to construct a local instance of blitz. When you have + * completed your usage of this local instance invoke either: + * + * <ul> + * <li>Shutdown - which will cleanly shutdown the instance retaining state + * (assuming you're running blitz in persistent mode).</li> + * <li>Destroy - which will cleanly shutdown the instance whilst discarding + * state as defined in the Jini DestroyAdmin specifications (note you can + * cause destroy to behave similarly to shutdown via appropriate + * configuration.</li> + * </ul> + */ + public BlitzServiceImpl() throws RemoteException, ConfigurationException { + init(new String[] {}, false); + } + + private void init(String[] anArgs, boolean doExport) + throws ConfigurationException, RemoteException { + + if (doExport) { + if (System.getSecurityManager() == null) + System.setSecurityManager(new RMISecurityManager()); + } + + if ((anArgs != null) && (anArgs.length > 0)) { + ConfigurationFactory.setup(anArgs); + } + + try { + doCompatDestroy = + ((Boolean) ConfigurationFactory.getEntry("compliantDestroy", + Boolean.class, + new Boolean(false))).booleanValue(); + + theLoginContext = (LoginContext) + ConfigurationFactory.getEntry("loginContext", + LoginContext.class, null); + + if (theLoginContext != null) { + try { + theLoginContext.login(); + } catch (LoginException aLE) { + theLogger.log(Level.SEVERE, "Couldn't login", aLE); + throw new ConfigurationException("Login invalid", aLE); + } + } + + doPriv(new PrivilegedInitImpl(doExport)); + } catch (Exception anE) { + theLogger.log(Level.SEVERE, "Oops privilege problem?", anE); + throw new ConfigurationException("loginContext has insufficient privileges", anE); + } + } + + private Object doPriv(PrivilegedExceptionAction aPAE) + throws Exception { + + if (theLoginContext != null) { + return Subject.doAsPrivileged(theLoginContext.getSubject(), + aPAE, null); + } else + return aPAE.run(); + } + + private class PrivilegedInitImpl implements PrivilegedExceptionAction { + private boolean doExport; + + PrivilegedInitImpl(boolean shouldExport) { + doExport = shouldExport; + } + + public Object run() throws Exception { + try { + initImpl(doExport); + } catch (Exception anE) { + theLogger.log(Level.SEVERE, "Could init with subject", anE); + throw anE; + } + return null; + } + } + + private void initImpl(boolean doExport) + throws ConfigurationException, RemoteException { + + /* + Create space with a TxnGatewayImpl + Once we've got as far as export'ing, invoke setParticipantStub + on TxnGatewayImpl so it can bundle things up appropriately. + */ + + // Make sure storage is able to initialize before we do anything + // + StoragePersonalityFactory.getPersonality(); + + theLookupStore = new LookupStorage(); + theLookupStore.init(ConfigurationFactory.getConfig()); + + Configuration myConfig = ConfigurationFactory.getConfig(); + + Exporter myDefaultExporter = + new BasicJeriExporter(TcpServerEndpoint.getInstance(0), + new BasicILFactory(), false, true); + + if (theActivationID == null) { + theExporter = + (Exporter) ConfigurationFactory.getEntry("serverExporter", + Exporter.class, + myDefaultExporter); + } else { + ProxyPreparer myIDPreparer = + ConfigurationFactory.getPreparer("activationIdPreparer"); + + ProxyPreparer mySysPreparer = + ConfigurationFactory.getPreparer("activationSysPreparer"); + + theActivationID = + (ActivationID) myIDPreparer.prepareProxy(theActivationID); + + try { + theActivationSystem = + (ActivationSystem) mySysPreparer.prepareProxy(ActivationGroup.getSystem()); + } catch (ActivationException anAE) { + throw new RemoteException("Unable to locate ActivationSystem"); + } + + ActivationExporter myDefActExp = + new ActivationExporter(theActivationID, myDefaultExporter); + + theExporter = + (Exporter) ConfigurationFactory.getEntry("serverExporter", + Exporter.class, + myDefActExp, + theActivationID); + } + + if (theExporter == null) { + throw new ConfigurationException("serverExporter must be non-null if it's specified"); + } + + theLogger.log(Level.INFO, "Using exporter: " + theExporter); + + if (doExport) + theStub = (BlitzServer) theExporter.export(this); + else { + // Don't need this - so clear it down.... + // + theExporter = null; + + theStub = this; + } + + Class[] myInterfaces = theStub.getClass().getInterfaces(); + + for (int i = 0; i < myInterfaces.length; i++) { + theLogger.log(Level.INFO, + "Stub supports: " + myInterfaces[i].getName()); + } + + theAdminProxy = + ProxyFactory.newAdminProxy(theStub, theLookupStore.getUuid()); + + theSpaceProxy = + ProxyFactory.newBlitzProxy(theStub, theLookupStore.getUuid()); + + theTxnProxy = + ProxyFactory.newTxnParticipantProxy(theStub, + theLookupStore.getUuid()); + theTxnGate = + new TxnGatewayImpl(theTxnProxy); + + RemoteEventDispatcher.setSource(theSpaceProxy); + + try { + theSpace = new SpaceImpl(theTxnGate); + + // Now run the user initializers + // + ColocatedAgent[] myInitializers = + (ColocatedAgent[]) + ConfigurationFactory.getEntry( + "agents", ColocatedAgent[].class, + new ColocatedAgent[0]); + + for (int i = 0; i < myInitializers.length; i++) { + myInitializers[i].init(theSpaceProxy); + } + + } catch (Exception anE) { + theLogger.log(Level.SEVERE, "Failed to start space", anE); + throw new RemoteException("Failed to start space", anE); + } + + if (doExport) { + theLogger.log(Level.INFO, "Space core is up, starting JoinManager"); + + try { + theJoinManager = new JoinManager(theSpaceProxy, + theLookupStore.getAttributes(), + theLookupStore.getServiceID(), + theLookupStore.getDiscMgt(), + null, + myConfig); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "Failed to init JoinManager", + anIOE); + + throw new RemoteException("Failed to init JoinManager", + anIOE); + } + } + + try { + theLoopbackTxnMgr = + new TransactionManagerImpl(theStub, theLookupStore.getUuid()); + } catch (IOException anIOE) { + throw new RemoteException("Failed to init loopback txn manager", + anIOE); + } + } + + public JavaSpace getSpaceProxy() { + return theSpaceProxy; + } + + public SpaceImpl getSpace() { + return theSpace; + } + + public LeaseImpl write(MangledEntry anEntry, Transaction aTxn, + long aLeaseTime) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + WriteTicket myTicket = theSpace.write(anEntry, aTxn, aLeaseTime); + + return + ProxyFactory.newEntryLeaseImpl(theStub, theLookupStore.getUuid(), + myTicket.getUID(), + myTicket.getExpirationTime()); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "SpaceImpl has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + public MangledEntry take(MangledEntry anEntry, Transaction aTxn, + long aWaitTime) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + return theSpace.take(anEntry, aTxn, aWaitTime); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "SpaceImpl has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + public MangledEntry read(MangledEntry anEntry, Transaction aTxn, + long aWaitTime) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + return theSpace.read(anEntry, aTxn, aWaitTime); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "SpaceImpl has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + public MangledEntry takeIfExists(MangledEntry anEntry, Transaction aTxn, + long aWaitTime) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + return theSpace.takeIfExists(anEntry, aTxn, aWaitTime); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "SpaceImpl has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + public MangledEntry readIfExists(MangledEntry anEntry, Transaction aTxn, + long aWaitTime) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + return theSpace.readIfExists(anEntry, aTxn, aWaitTime); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "SpaceImpl has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + public EventRegistration notify(MangledEntry anEntry, Transaction aTxn, + RemoteEventListener aListener, + long aLeaseTime, + MarshalledObject aHandback) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + RegTicket myTicket = + theSpace.notify(anEntry, aTxn, aListener, aLeaseTime, + aHandback); + + LeaseImpl myLease = + ProxyFactory.newLeaseImpl(theStub, theLookupStore.getUuid(), + myTicket.getUID(), + myTicket.getExpirationTime()); + + return new EventRegistration(myTicket.getSourceId(), theSpaceProxy, + myLease, myTicket.getSeqNum()); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "SpaceImpl has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + /* ********************************************************************* + * ProxyAccessor stuff + ******************************************************************** */ + + public Object getProxy() { + return theStub; + } + + /* ********************************************************************* + * ServerProxyTrust stuff + ******************************************************************** */ + + /** + <p> Blitz can be run as a secure service, in which case, it exports + various ConstrainedProxy instances which support RemoteMethodControl. + Such proxy instances are exported only when the original stub supports + RemoteMethodControl. </p> + + <p> As Blitz uses custom proxies as opposed to JERI stubs, we must + do some additional work to support TrustVerification. Assuming the + stub has been generated using ProxyTrustILFactory it supports the + bootstrap step. For bootstrap to work, the remote server must support + ServerProxyTrust which will be invoked as part of the bootstrap process + supported in the stub. </p> + */ + public TrustVerifier getProxyVerifier() throws RemoteException { + stopBarrier(); + + return new ProxyVerifier(theStub, theLookupStore.getUuid()); + } + + /* ********************************************************************* + * Transaction participant stuff + ******************************************************************** */ + + public int prepare(TransactionManager mgr, long id) + throws UnknownTransactionException, RemoteException { + + stopBarrier(); + + return theSpace.getTxnControl().prepare(mgr, id); + } + + public void commit(TransactionManager mgr, long id) + throws UnknownTransactionException, RemoteException { + + stopBarrier(); + + theSpace.getTxnControl().commit(mgr, id); + } + + public void abort(TransactionManager mgr, long id) + throws UnknownTransactionException, RemoteException { + + stopBarrier(); + + theSpace.getTxnControl().abort(mgr, id); + } + + public int prepareAndCommit(TransactionManager mgr, long id) + throws UnknownTransactionException, RemoteException { + + stopBarrier(); + + return theSpace.getTxnControl().prepareAndCommit(mgr, id); + } + + /* ********************************************************************* + * Landlord + ******************************************************************** */ + public long renew(SpaceUID aUID, long aDuration) + throws UnknownLeaseException, LeaseDeniedException, RemoteException { + + // System.out.println("Renew: " + aUID + ", " + aDuration); + + stopBarrier(); + + try { + long myResult = theSpace.getLeaseControl().renew(aUID, aDuration); + + // System.out.println("Renew: " + myResult); + + return myResult; + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "Space has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + public void cancel(SpaceUID aUID) + throws UnknownLeaseException, RemoteException { + + stopBarrier(); + + try { + theSpace.getLeaseControl().cancel(aUID); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "Space has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + public LeaseResults renew(SpaceUID[] aLeases, long[] aDurations) + throws RemoteException { + + stopBarrier(); + + long[] myNewDurations = new long[aLeases.length]; + Exception[] myFails = null; + + for (int i = 0; i < aLeases.length; i++) { + try { + myNewDurations[i] = + theSpace.getLeaseControl().renew(aLeases[i], + aDurations[i]); + } catch (LeaseDeniedException anLDE) { + if (myFails == null) + myFails = initFails(aLeases.length, i); + + myNewDurations[i] = -1; + myFails[i] = anLDE; + } catch (UnknownLeaseException anULE) { + if (myFails == null) + myFails = initFails(aLeases.length, i); + + myNewDurations[i] = -1; + myFails[i] = anULE; + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "Space has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + return new LeaseResults(myNewDurations, myFails); + } + + public LeaseResults cancel(SpaceUID[] aLeases) + throws RemoteException { + + stopBarrier(); + + Exception[] myFails = null; + + for (int i = 0; i < aLeases.length; i++) { + try { + theSpace.getLeaseControl().cancel(aLeases[i]); + } catch (UnknownLeaseException aULE) { + if (myFails == null) + myFails = initFails(aLeases.length, i); + + myFails[i] = aULE; + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "Space has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + if (myFails != null) + return new LeaseResults(null, myFails); + else + return null; + } + + private Exception[] initFails(int aLength, int anOffset) { + return new Exception[aLength]; + } + + /* ********************************************************************* + * ServiceProxyAccessor + ******************************************************************** */ + + public Object getServiceProxy() throws RemoteException { + stopBarrier(); + + return theSpaceProxy; + } + + /* ********************************************************************* + * Administrable::getAdmin support + ******************************************************************** */ + + public Object getAdmin() throws RemoteException { + stopBarrier(); + + return theAdminProxy; + } + + /* ********************************************************************* + * JoinAdmin + ******************************************************************** */ + /** + * Get the current attribute sets for the service. + * + * @return the current attribute sets for the service + * @throws java.rmi.RemoteException + */ + public Entry[] getLookupAttributes() throws RemoteException { + stopBarrier(); + + return theJoinManager.getAttributes(); + } + + /** + * Add attribute sets for the service. The resulting set will be used + * for all future joins. The attribute sets are also added to all + * currently-joined lookup services. + * + * @param attrSets the attribute sets to add + * @throws java.rmi.RemoteException + */ + public void addLookupAttributes(Entry[] attrSets) throws RemoteException { + stopBarrier(); + + theJoinManager.addAttributes(attrSets); + syncStore(); + } + + /** + * Modify the current attribute sets, using the same semantics as + * ServiceRegistration.modifyAttributes. The resulting set will be used + * for all future joins. The same modifications are also made to all + * currently-joined lookup services. + * + * @param attrSetTemplates the templates for matching attribute sets + * @param attrSets the modifications to make to matching sets + * @throws java.rmi.RemoteException + * + * @see net.jini.core.lookup.ServiceRegistration#modifyAttributes + */ + public void modifyLookupAttributes(Entry[] attrSetTemplates, + Entry[] attrSets) + throws RemoteException { + stopBarrier(); + + theJoinManager.modifyAttributes(attrSetTemplates, attrSets); + syncStore(); + } + + private DiscoveryGroupManagement getDGM() { + return (DiscoveryGroupManagement) theJoinManager.getDiscoveryManager(); + } + + /** + * Get the list of groups to join. An empty array means the service + * joins no groups (as opposed to "all" groups). + * + * @return an array of groups to join. An empty array means the service + * joins no groups (as opposed to "all" groups). + * @throws java.rmi.RemoteException + * @see #setLookupGroups + */ + public String[] getLookupGroups() throws RemoteException { + stopBarrier(); + + return getDGM().getGroups(); + } + + /** + * Add new groups to the set to join. Lookup services in the new + * groups will be discovered and joined. + * + * @param groups groups to join + * @throws java.rmi.RemoteException + * @see #removeLookupGroups + */ + public void addLookupGroups(String[] groups) throws RemoteException { + stopBarrier(); + + try { + getDGM().addGroups(groups); + } catch (IOException anIOE) { + throw new RemoteException("Got IOE changing groups", anIOE); + } + syncStore(); + } + + /** + * Remove groups from the set to join. Leases are cancelled at lookup + * services that are not members of any of the remaining groups. + * + * @param groups groups to leave + * @throws java.rmi.RemoteException + * @see #addLookupGroups + */ + public void removeLookupGroups(String[] groups) throws RemoteException { + stopBarrier(); + + getDGM().removeGroups(groups); + syncStore(); + } + + /** + * Replace the list of groups to join with a new list. Leases are + * cancelled at lookup services that are not members of any of the + * new groups. Lookup services in the new groups will be discovered + * and joined. + * + * @param groups groups to join + * @throws java.rmi.RemoteException + * @see #getLookupGroups + */ + public void setLookupGroups(String[] groups) throws RemoteException { + stopBarrier(); + + try { + getDGM().setGroups(groups); + } catch (IOException anIOE) { + throw new RemoteException("Got IOE changing groups", anIOE); + } + syncStore(); + } + + private DiscoveryLocatorManagement getDLM() { + return (DiscoveryLocatorManagement) + theJoinManager.getDiscoveryManager(); + } + + /** + *Get the list of locators of specific lookup services to join. + * + * @return the list of locators of specific lookup services to join + * @throws java.rmi.RemoteException + * @see #setLookupLocators + */ + public LookupLocator[] getLookupLocators() throws RemoteException { + stopBarrier(); + + return getDLM().getLocators(); + } + + /** + * Add locators for specific new lookup services to join. The new + * lookup services will be discovered and joined. + * + * @param locators locators of specific lookup services to join + * @throws java.rmi.RemoteException + * @see #removeLookupLocators + */ + public void addLookupLocators(LookupLocator[] locators) + throws RemoteException { + stopBarrier(); + + getDLM().addLocators(locators); + syncStore(); + } + + /** + * Remove locators for specific lookup services from the set to join. + * Any leases held at the lookup services are cancelled. + * + * @param locators locators of specific lookup services to leave + * @throws java.rmi.RemoteException + * @see #addLookupLocators + */ + public void removeLookupLocators(LookupLocator[] locators) + throws RemoteException { + stopBarrier(); + + getDLM().removeLocators(locators); + syncStore(); + } + + /** + * Replace the list of locators of specific lookup services to join + * with a new list. Leases are cancelled at lookup services that were + * in the old list but are not in the new list. Any new lookup services + * will be discovered and joined. + * + * @param locators locators of specific lookup services to join + * @throws java.rmi.RemoteException + * @see #getLookupLocators + */ + public void setLookupLocators(LookupLocator[] locators) + throws RemoteException { + stopBarrier(); + + getDLM().setLocators(locators); + syncStore(); + } + + private void syncStore() throws RemoteException { + try { + theLookupStore.sync(theJoinManager); + } catch (IOException anIOE) { + throw new RemoteException("Couldn't update saved state", anIOE); + } + } + + + /* ********************************************************************* + * DestroyAdmin + ******************************************************************** */ + + public void destroy() throws RemoteException { + destroyImpl(doCompatDestroy); + } + + private void destroyImpl(boolean doCleanup) throws RemoteException { + stopBarrier(); + + blockCalls(); + + // Load cleanup variable from config and construct accordingly + Thread myDestroyer = new DestroyThread(doCleanup); + myDestroyer.setDaemon(false); + myDestroyer.start(); + } + + private void blockCalls() { + synchronized(this) { + isStopped = true; + } + } + + private void unblockCalls() { + synchronized(this) { + isStopped = false; + } + } + + /** + Used to check whether calls are blocked and, if they are, throws + a RemoteException back to the client. + */ + private void stopBarrier() throws RemoteException { + synchronized(this) { + if (isStopped) + throw new RemoteException("Remote calls not permitted"); + } + } + + private class DestroyThread extends Thread { + private boolean doClean; + + private DestroyThread(boolean shouldClean) { + super("DestroyThread"); + doClean = shouldClean; + } + + public void run() { + theLogger.log(Level.SEVERE, "Shutdown in progress"); + + if (theLoopbackTxnMgr != null) { + theLogger.log(Level.SEVERE, "Stop LoopbackTxnMgr"); + theLoopbackTxnMgr.terminate(); + } + + if (theJoinManager != null) { + theLogger.log(Level.SEVERE, "Stop JoinManager"); + DiscoveryManagement myDGM = theJoinManager.getDiscoveryManager(); + + theJoinManager.terminate(); + myDGM.terminate(); + } + + if (theActivationID != null) { + theLogger.log(Level.SEVERE, + "De-registering from ActivationSystem"); + + try { + theActivationSystem.unregisterObject(theActivationID); + } catch (Exception anAE) { + theLogger.log(Level.SEVERE, "Activation unregister failed", + anAE); + } + } + + if ((theStub != null) && (theExporter != null)) { + theLogger.log(Level.SEVERE, "Unexport - forced"); + theExporter.unexport(true); + } + + if (theSpace != null) { + try { + theLogger.log(Level.SEVERE, "Stopping space"); + theSpace.stop(); + } catch (Exception anE) { + theLogger.log(Level.SEVERE, "Space failed to shutdown cleanly", + anE); + } + } + + if (theActivationID != null) { + theLogger.log(Level.SEVERE, "Notifying ActivationGroup"); + + try { + Activatable.inactive(theActivationID); + } catch (Exception anE) { + theLogger.log(Level.SEVERE, "ActivationGroup complained", + anE); + } + } + + if (theLifecycle != null) { + theLogger.log(Level.SEVERE, "Lifecycle::unregister"); + theLifecycle.unregister(theServiceImpl); + } + + if (theLoginContext != null) { + try { + theLogger.log(Level.SEVERE, "Logout"); + theLoginContext.logout(); + } catch (LoginException aLE) { + theLogger.log(Level.SEVERE, "Couldn't logout", aLE); + } + } + + theLogger.log(Level.SEVERE, "Shutdown complete"); + + /* + Check config and optionally invoke Disk::clear on + appropriate directories + */ + if (doClean) { + StoragePersonality myPersonality = + StoragePersonalityFactory.getPersonality(); + + /* + It's possible that config is broken and thus we are + performing destroy. If config *is* broken, we may not + be able to create a personality..... + */ + if (myPersonality != null) { + theLogger.log(Level.SEVERE, "Erasing disk state"); + + myPersonality.destroy(); + } + } + } + } + + /* ********************************************************************* + * StatsAdmin + ******************************************************************** */ + + public Stat[] getStats() throws RemoteException { + stopBarrier(); + + return StatsBoard.get().getStats(); + } + + public void setSwitches(Switch[] aListOfSwitches) throws RemoteException { + stopBarrier(); + + SwitchSettings.get().update(aListOfSwitches); + } + + /* ********************************************************************* + * EntryViewAdmin + ******************************************************************** */ + public JavaSpace getJavaSpaceProxy() throws RemoteException { + return theSpaceProxy; + } + + public ViewResult newView(MangledEntry[] aTemplates, Transaction aTxn, + long aLeaseDuration, boolean isJavaSpace05, + long aLimit, int anInitialChunk) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + long myDuration = aLeaseDuration; + + /* + Bound the lease if we're instructed to do so - we may not + be required to if this is an emulation of the old + JavaSpaceAdmin::contents call + */ + if (isJavaSpace05) + myDuration = LeaseBounds.boundView(aLeaseDuration); + + /* + * JavaSpace05 holds locks only for a non-null transaction. + * JavaSpaceAdmin never holds locks but uses the transaction to + * test visibility + */ + boolean holdLocks = + (isJavaSpace05) ? (aTxn != null) : false; + + /* + If we're being asked to do JavaSpace05, we're performing the new contents + operation which means we should pass true for holdlocks, false otherwise + */ + ViewRegistration myReg = + EntryViewFactory.get().newView(aTemplates, aTxn, holdLocks, + myDuration, aLimit, theSpace); + + /* + Okay, got a view, now we need an initial batch + */ + EntryChit[] myInitialBatch = getNext(myReg.getUID(), anInitialChunk); + + return new ViewResult( + ProxyFactory.newLeaseImpl(theStub, theLookupStore.getUuid(), + myReg.getUID(), + myReg.getExpiry()), + myInitialBatch); + } catch (IOException anIOE) { + throw new RemoteException("Failed to create view", anIOE); + } + } + + public EntryChit[] getNext(EntryViewUID aUid, int aChunkSize) + throws RemoteException { + + stopBarrier(); + + ArrayList myNext = new ArrayList(); + + EntryView myView = EntryViewFactory.get().getView(aUid); + + /* + View could have expired or been otherwise lost + */ + if (myView == null) + throw new NoSuchObjectException("View has been lost"); + + try { + for (int i = 0; i < aChunkSize; i++) { + EntryChit myChit = myView.next(); + + if (myChit == null) + break; + else + myNext.add(myChit); + } + } catch (TransactionException aTE) { + throw new RemoteException("View was prematurely destroyed - was a transaction ended?", aTE); + } catch (IOException anIOE) { + throw new RemoteException("Couldn't recover an Entry", anIOE); + } + + if (myNext.size() == 0) + return null; + + EntryChit[] myChits = new EntryChit[myNext.size()]; + myChits = (EntryChit[]) myNext.toArray(myChits); + + return myChits; + } + + /** + Deletes a specific entity returned from an + <code>EntryView</code> via an <code>EntryChit</code> + */ + public void delete(Object aCookie) throws RemoteException { + stopBarrier(); + + try { + theSpace.getLeaseControl().cancel((SpaceUID) aCookie); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "Space has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } catch (UnknownLeaseException aULE) { + // It's gone already, fail silently + } + } + + public void close(EntryViewUID aUid) throws RemoteException { + stopBarrier(); + + EntryViewFactory.get().delete(aUid); + } + + /* ********************************************************************* + * JavaSpace05 + ******************************************************************** */ + + public List write(List aMangledEntries, Transaction aTxn, List aLeaseTimes) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + List myTickets = theSpace.write(aMangledEntries, aTxn, aLeaseTimes); + + for (int i = 0; i < myTickets.size(); i++) { + WriteTicket myTicket = (WriteTicket) myTickets.get(i); + + Lease myLease = + ProxyFactory.newEntryLeaseImpl(theStub, theLookupStore.getUuid(), + myTicket.getUID(), + myTicket.getExpirationTime()); + + myTickets.set(i, myLease); + } + + return myTickets; + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "SpaceImpl has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + public List take(MangledEntry[] aTemplates, Transaction aTxn, + long aWaitTime, long aLimit) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + return theSpace.take(aTemplates, aTxn, aWaitTime, aLimit); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "SpaceImpl has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + public EventRegistration + registerForVisibility(MangledEntry[] aTemplates, Transaction aTxn, + RemoteEventListener aListener, long aLeaseTime, + MarshalledObject aHandback, + boolean visibilityOnly) + throws RemoteException, TransactionException { + + stopBarrier(); + + try { + RegTicket myTicket = + theSpace.visibility(aTemplates, aTxn, aListener, aLeaseTime, + aHandback, visibilityOnly); + + LeaseImpl myLease = + ProxyFactory.newLeaseImpl(theStub, theLookupStore.getUuid(), + myTicket.getUID(), + myTicket.getExpirationTime()); + + return new EventRegistration(myTicket.getSourceId(), theSpaceProxy, + myLease, myTicket.getSeqNum()); + } catch (IOException anIOE) { + theLogger.log(Level.SEVERE, "SpaceImpl has disk problems", anIOE); + throw new RemoteException("Space has disk problems", anIOE); + } + } + + /* ********************************************************************* + * BlitzAdmin + ******************************************************************** */ + public void requestSnapshot() throws RemoteException, + TransactionException, IOException { + stopBarrier(); + theSpace.getTxnControl().requestSnapshot(); + } + + public void shutdown() throws RemoteException { + // stopBarrier is donw in destroyImpl + destroyImpl(false); + } + + public void backup(String aDir) throws RemoteException, IOException { + stopBarrier(); + theSpace.getTxnControl().backup(aDir); + } + + public void clean() throws RemoteException, IOException { + stopBarrier(); + + blockCalls(); + + try { + theSpace.empty(); + } finally { + unblockCalls(); + } + } + + public void reap() throws RemoteException { + theSpace.reap(); + } +}