You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2014/05/09 09:03:19 UTC
svn commit: r1593493 [12/24] - in /river/jtsk/skunk/qa_refactor/trunk: qa/
qa/src/com/sun/jini/test/impl/end2end/jssewrapper/
qa/src/com/sun/jini/test/impl/joinmanager/
qa/src/com/sun/jini/test/impl/mahalo/
qa/src/com/sun/jini/test/impl/outrigger/match...
Modified: river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/norm/NormServerBaseImpl.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/norm/NormServerBaseImpl.java?rev=1593493&r1=1593492&r2=1593493&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/norm/NormServerBaseImpl.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/norm/NormServerBaseImpl.java Fri May 9 07:03:18 2014
@@ -1,1995 +1,1995 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.sun.jini.norm;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.rmi.MarshalledObject;
-import java.rmi.NoSuchObjectException;
-import java.rmi.RemoteException;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-
-import net.jini.config.Configuration;
-import net.jini.config.ConfigurationProvider;
-import net.jini.core.constraint.RemoteMethodControl;
-import net.jini.core.discovery.LookupLocator;
-import net.jini.core.entry.Entry;
-import net.jini.core.event.EventRegistration;
-import net.jini.core.event.RemoteEvent;
-import net.jini.core.event.RemoteEventListener;
-import net.jini.core.lease.Lease;
-import net.jini.core.lease.LeaseDeniedException;
-import net.jini.core.lease.UnknownLeaseException;
-import net.jini.core.lookup.ServiceID;
-import net.jini.export.Exporter;
-import net.jini.export.ProxyAccessor;
-import net.jini.id.ReferentUuid;
-import net.jini.id.Uuid;
-import net.jini.id.UuidFactory;
-import net.jini.lease.LeaseRenewalEvent;
-import net.jini.lease.LeaseRenewalManager;
-import net.jini.lease.LeaseRenewalService;
-import net.jini.lease.LeaseRenewalSet;
-import net.jini.lookup.entry.ServiceInfo;
-import net.jini.security.ProxyPreparer;
-import net.jini.security.TrustVerifier;
-import net.jini.security.proxytrust.ServerProxyTrust;
-import net.jini.security.proxytrust.TrustEquivalence;
-
-import com.sun.jini.constants.ThrowableConstants;
-import com.sun.jini.constants.VersionConstants;
-import com.sun.jini.landlord.Landlord.RenewResults;
-import com.sun.jini.landlord.LandlordUtil;
-import com.sun.jini.landlord.LeaseFactory;
-import com.sun.jini.landlord.LeasePeriodPolicy;
-import com.sun.jini.landlord.LocalLandlord;
-import com.sun.jini.lookup.entry.BasicServiceType;
-import com.sun.jini.norm.event.EventType;
-import com.sun.jini.norm.event.EventTypeGenerator;
-import com.sun.jini.norm.event.SendMonitor;
-import com.sun.jini.norm.lookup.JoinState;
-import com.sun.jini.proxy.ThrowThis;
-import com.sun.jini.start.LifeCycle;
-import com.sun.jini.reliableLog.LogException;
-import com.sun.jini.reliableLog.LogHandler;
-import org.apache.river.api.util.Startable;
-import com.sun.jini.thread.InterruptedStatusThread;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import net.jini.loader.ClassLoading;
-
-/**
- * Base class for implementations of NormServer. Provides a complete
- * non-activatable (but still logging) implementation.
- *
- * @author Sun Microsystems, Inc.
- */
-abstract class NormServerBaseImpl
- implements NormServer, LocalLandlord, ServerProxyTrust, ProxyAccessor, Startable
-{
- /** Current version of log format */
- private static final int CURRENT_LOG_VERSION = 2;
-
- /** Logger and configuration component name for Norm */
- static final String NORM = "com.sun.jini.norm";
-
- /** Logger for logging information about this instance */
- static final Logger logger = Logger.getLogger(NORM);
-
- /** Whether this server is persistent. */
- final boolean persistent;
-
- /** The login context, for logging out */
- final LoginContext loginContext;
-
- /** The location of our persistent storage, or null if not persistent. */
- final String persistenceDirectory;
-
- /** Proxy preparer for leases supplied through the API */
- final private ProxyPreparer leasePreparer;
-
- /**
- * Proxy preparer for leases recovered from persistent storage, or null if
- * not persistent.
- */
- final private ProxyPreparer recoveredLeasePreparer;
-
- /** Proxy preparer for listeners supplied through the API */
- final private ProxyPreparer listenerPreparer;
-
- /**
- * Proxy preparer for listeners recovered from persistent storage, or null
- * if not persistent.
- */
- final private ProxyPreparer recoveredListenerPreparer;
-
- /**
- * Proxy preparer for lookup locators supplied through the API, and not
- * including initial lookup locators.
- */
- final private ProxyPreparer locatorPreparer;
-
- /**
- * Proxy preparer for lookup locators recovered from persistent storage, or
- * null if not persistent.
- */
- final private ProxyPreparer recoveredLocatorPreparer;
-
- /** The exporter for exporting and unexporting */
- final Exporter exporter;
-
- /** Object to notify when this service is destroyed, or null */
- final private LifeCycle lifeCycle;
-
- /** The unique ID for this server. */
- volatile private Uuid serverUuid;
-
- /** Our JoinManager */
- volatile private JoinState joinState;
-
- /** Map of Uuids to LeaseSets */
- final private Map<Uuid,LeaseSet> setTable = Collections.synchronizedMap(new HashMap<Uuid,LeaseSet>());
-
- /** Lease renewal manager that actually renews the leases */
- final private LeaseRenewalManager lrm;
-
- /** Object that expires sets and generates expiration warning events */
- final private LeaseExpirationMgr expMgr;
-
- /** Factory for creating leases */
- volatile private LeaseFactory leaseFactory;
-
- /** Policy we use for granting and renewing renewal set leases */
- final private LeasePeriodPolicy setLeasePolicy;
-
- /**
- * Whether to isolate leases in their own renewal sets as opposed to
- * batching leases across sets.
- */
- final private boolean isolateSets;
-
- /** Our persistant store */
- volatile private PersistentStore store;
-
- /** Factory we use to create ClientLeaseWrapper IDs */
- final private UIDGenerator idGen = new UIDGenerator() ;
-
- /** List of leases that have been renewed but not persisted */
- final private List renewedList;
-
- /**
- * Thread that pulls wrapped client leases off the renewedList and logs
- * them to disk
- */
- final private RenewLogThread renewLogger;
-
- /** Object used to generate new event types */
- volatile private EventTypeGenerator generator;
-
- /**
- * Object that transfers events from the renewal manager to
- * us so we can remove dead leases and send events
- */
- final private LRMEventListener lrmEventListener;
-
- /** Log file must contain this many records before snapshot allowed */
- final private int logToSnapshotThresh;
-
- /** Weight factor applied to snapshotSize when deciding to take snapshot */
- final private float snapshotWt;
-
- /** Inner server proxy */
- volatile NormServer serverProxy = null;
-
- /** Outer service proxy */
- volatile LeaseRenewalService normProxy = null;
-
- /** Admin proxy */
- volatile private AdminProxy adminProxy;
-
- /** Thread that performs snapshots when signaled */
- volatile private SnapshotThread snapshotter;
-
- /** Lock protecting startup and shutdown */
- private final ReadyState ready = new ReadyState();
-
- /** Keep track of the number of leases. */
- private final CountLeases countLeases = new CountLeases();
-
- /** Fields access only by thread that called constructor, then called start()
- * set to null at completion of start() */
- private AccessControlContext context;
- private Configuration config;
-
- private boolean started;
-
- ////////////////////////////////
- // Methods defined in NormServer
-
- // Inherit java doc from super type
- public void renewFor(Uuid id, Lease leaseToRenew,
- long membershipDuration, long renewDuration)
- throws RemoteException, ThrowThis
- {
- ready.check();
-
- // Lookup the set
- final LeaseSet set = getSet(id);
-
- if (leaseToRenew == null) {
- throw new NullPointerException("LeaseRenewalSet.renewFor:Must " +
- "pass a non-null lease");
- }
-
- if ((membershipDuration != Lease.FOREVER) &&
- (renewDuration == Lease.ANY))
- {
- throw new IllegalArgumentException(
- "LeaseRenewalSet.renewFor:renewDuration can only be " +
- "Lease.ANY if membershipDuration is Lease.FOREVER");
- }
-
- if (!(renewDuration == Lease.ANY || renewDuration == Lease.FOREVER ||
- renewDuration > 0))
- {
- throw new IllegalArgumentException(
- "LeaseRenewalSet.renewFor:renewDuration can only be " +
- "Lease.ANY, Lease.FOREVER, or positive");
- }
-
- leaseToRenew = (Lease) leasePreparer.prepareProxy(leaseToRenew);
-
- // Ensure that the lease is not a current lease granted by this server
- if (leaseToRenew instanceof ReferentUuid) {
- Uuid cookie = ((ReferentUuid) leaseToRenew).getReferentUuid();
- LeaseSet setForLease = (LeaseSet) setTable.get(cookie);
- if (setForLease != null) {
- synchronized (setForLease) {
- if (isCurrent(setForLease)) {
- throw new IllegalArgumentException(
- "Cannot add leases granted by a " +
- "LeaseRenewalService to a set created by " +
- "that service");
- }
- }
- }
- }
-
- // If we are told to dump the codebase of the lease we are adding
- if (logger.isLoggable(Level.FINE)) {
- final Class lc = leaseToRenew.getClass();
- logger.log(Level.FINE,
- "Adding lease of class {0} with annotation {1}",
- new Object[] {
- leaseToRenew.getClass(),
- ClassLoading.getClassAnnotation(lc) });
- }
-
- // Add the lease to the set
- add(set, leaseToRenew, membershipDuration, renewDuration);
- }
-
- /**
- * Prevents access to the service before it is ready or after it starts to
- * shutdown. Each public entrypoint to the service should call check or
- * shutdown, and initialization should call ready when the service is ready
- * to use.
- */
- private static final class ReadyState {
- private static final int INITIALIZE = 0;
- private static final int READY = 1;
- private static final int SHUTDOWN = 2;
- private int state = INITIALIZE;
-
- /**
- * Checks if the service is ready to use, waiting if it is
- * initializing, and throwing IllegalStateException if it is shutting
- * down.
- */
- synchronized void check() {
- while (true) {
- switch (state) {
- case INITIALIZE:
- try {
- wait();
- } catch (InterruptedException e) {
- }
- break;
- case READY:
- return;
- default:
- throw new IllegalStateException(
- "Norm service is unavailable");
- }
- }
- }
-
- /**
- * Marks the service ready for use, throwing IllegalStateException if
- * it is shutting down.
- */
- synchronized void ready() {
- switch (state) {
- case INITIALIZE:
- state = READY;
- notifyAll();
- break;
- case READY:
- break;
- default:
- throw new IllegalStateException("Norm service is unavailable");
- }
- }
-
- /**
- * Marks the service as shutting down, waiting if it is initializing,
- * and throwing IllegalStateException if it is already shutting down.
- */
- synchronized void shutdown() {
- check();
- state = SHUTDOWN;
- notifyAll();
- }
- }
-
- /** Keeps track of the number of leases. */
- private static class CountLeases {
- private int count;
-
- private synchronized void updateCount(int change) {
- count += change;
- assert count >= 0;
- }
-
- private synchronized int getCount() {
- return count;
- }
- }
-
- /** Update the number of leases being managed by this server. */
- void updateLeaseCount(int change) {
- countLeases.updateCount(change);
- }
-
- /**
- * Add the lease to the set.
- *
- * @param set the LeaseSet to add the leaseToRenew to
- * @param leaseToRenew the lease the client wants managed
- * @param membershipDuration the length of time the client
- * wants the lease managed for
- * @param renewDuration the length of time the client want the
- * lease renewed for each time it is renewed
- * @throws ThrowThis if the set no longer exists
- */
- private void add(LeaseSet set, Lease leaseToRenew,
- long membershipDuration, long renewDuration)
- throws ThrowThis
- {
- try {
- store.acquireMutatorLock();
- synchronized (set) {
- ensureCurrent(set);
-
- // Get a wrapper for the lease
- final long now = System.currentTimeMillis();
- ClientLeaseWrapper clw =
- set.getClientLeaseWrapper(leaseToRenew);
- if (clw == null) {
- // We don't know about this lease, create a new wrapper
- try {
- clw = new ClientLeaseWrapper(
- leaseToRenew, idGen.newID(), renewedList, set,
- membershipDuration, renewDuration, now);
- } catch (IOException e) {
- throw new IllegalArgumentException(
- "NormServerBaseImpl.renewFor:Handed lease " +
- "that can't be marshalled");
- }
- } else {
- // We know about this lease -- update its stats
- clw.update(membershipDuration, renewDuration, now);
- }
-
- set.update(clw);
- lrm.renewUntil(clw, clw.getMembershipExpiration(),
- clw.getRenewDuration(), lrmEventListener);
-
- // $$$ What if this lease was just dropped by the LRM
- // (say because there was some problem renewing the
- // lease)? Are we going to lose the lease because
- // we will shortly process a renewalFailureEvent?
- // Is it a problem if we do? Presumably re-adding it
- // is not going to fix the underlying problem...
- }
- } finally {
- store.releaseMutatorLock();
- }
- }
-
-
- // Inherit java doc from super type
- public Lease remove(Uuid id, Lease leaseToRemove)
- throws RemoteException, ThrowThis
- {
- ready.check();
- final LeaseSet set = getSet(id);
-
- if (leaseToRemove == null) {
- throw new NullPointerException("LeaseRenewalSet.remove:Must " +
- "pass a non-null lease");
- }
-
- leaseToRemove = (Lease) leasePreparer.prepareProxy(leaseToRemove);
- logger.log(Level.FINE, "Removing lease {0}", leaseToRemove);
-
- // The most up-to-date ref to the lease we have
- Lease rslt = null;
- try {
- store.acquireMutatorLock();
- synchronized (set) {
- ensureCurrent(set);
-
- final ClientLeaseWrapper clw
- = set.getClientLeaseWrapper(leaseToRemove);
-
- if (clw == null) {
- // Lease must have been removed already
- return null;
- }
-
- try {
- lrm.remove(clw);
- } catch (UnknownLeaseException e) {
- // This can happen if there was some problem
- // renewing the lease or its LRM expiration just
- // ran out. Since we are removing the lease anyway
- // ignore.
- }
-
- final boolean present = set.remove(clw);
-
- // Only return a non-null result if the removed lease
- // had not be removed already
- if (present) {
- // At this point we can assume clw is no
- // longer deformed
- rslt = clw.getClientLease();
- }
- }
- } finally {
- store.releaseMutatorLock();
- }
-
- if (rslt == null)
- return null;
-
- // Whenever we serialize a lease we have to make sure that
- // its serial form will stay the same during the
- // serialization. Since we have removed it from the set we
- // don't have to worry about this lease being serialized to disk
- // any more so changing the serial format here should be safe.
- rslt.setSerialFormat(Lease.DURATION);
- return rslt;
- }
-
- // Inherit java doc from super type
- public GetLeasesResult getLeases(Uuid id) throws ThrowThis {
- ready.check();
- final LeaseSet set = getSet(id);
-
- // We are not modifying the set so we don't need the mutator lock
- // $$$ Do we need a reader lock, or is the lock on the set enough?
- // $$$ Need to make sure we really don't mutate any persistent state
- // or have issues with serializing the leases.
- synchronized (set) {
- ensureCurrent(set);
- return new GetLeasesResult(set.getLeases());
- }
- }
-
-
- // Inherit java doc from super type
- public EventRegistration setExpirationWarningListener(
- Uuid id,
- RemoteEventListener listener,
- long minWarning,
- MarshalledObject handback)
- throws RemoteException, ThrowThis
- {
- ready.check();
- final LeaseSet set = getSet(id);
-
- if (listener == null) {
- minWarning = NO_LISTENER;
- handback = null;
- } else if (minWarning < 0) {
- throw new IllegalArgumentException(
- "LeaseRenewalSet.setExpirationWarningListener:minWarning " +
- "must be positive");
- } else {
- listener = (RemoteEventListener) listenerPreparer.prepareProxy(
- listener);
- }
-
- try {
- store.acquireMutatorLock();
- synchronized (set) {
- ensureCurrent(set);
-
- try {
- final boolean haveBefore = set.haveWarningRegistration();
- final EventRegistration rslt =
- set.setExpirationWarningListener(
- listener, minWarning, handback);
- final boolean haveAfter = set.haveWarningRegistration();
-
- if (haveAfter || (haveBefore != haveAfter)) {
- // Either we had a registration before and we
- // don't now, we do now and not before, or we
- // had one before and we still do. In the
- // first two cases we have to wack the
- // expiration manager so it can schedule the
- // right task. In the last case wack the
- // expiration manager so it can reschedule in case
- // minWarning has changed.
- expMgr.reschedule(set);
- }
-
- return rslt;
- } catch (IOException e) {
- // This means the listener could not be serialized,
- // re-throw as an IllegalArgumentException
- throw new IllegalArgumentException("Passed a listener " +
- "that could not be serialized");
- }
- }
- } finally {
- store.releaseMutatorLock();
- }
- }
-
- /**
- * Remote a set if its expiration time has been reached.
- */
- void expireIfTime(LeaseSet set) {
- try {
- store.acquireMutatorLock();
-
- synchronized (set) {
- if (isCurrent(set)) {
- // Someone must have renewed the lease...don't expire
- return;
- }
-
- removeSet(set);
- }
- } finally {
- store.releaseMutatorLock();
- }
- }
-
- /**
- * Schedule the sending of an expiration warning event.
- * This could be a method on the set itself but this keeps all
- * of the high level synchronization logic in one file.
- */
- void sendWarningEvent(LeaseSet set) {
- // We don't need to acquire the store lock because we
- // won't be mutating any persistent state.
- // We will be reading state of the set so we do need to
- // sync on it to ensure we get a constant view
- synchronized (set) {
- if (!isCurrent(set)) {
- // Must have just been canceled or expired, don't send event
- return;
- }
-
- set.sendWarningEvent();
- }
- }
-
- // Inherit java doc from super type
- public EventRegistration setRenewalFailureListener(
- Uuid id,
- RemoteEventListener listener,
- MarshalledObject handback)
- throws RemoteException, ThrowThis
- {
- ready.check();
- final LeaseSet set = getSet(id);
-
- if (listener == null) {
- handback = null;
- } else {
- listener = (RemoteEventListener) listenerPreparer.prepareProxy(
- listener);
- }
-
- try {
- store.acquireMutatorLock();
- synchronized (set) {
- ensureCurrent(set);
-
- try {
- return set.setRenewalFailureListener(
- listener, handback);
- } catch (IOException e) {
- // This means the listener could not be serialized,
- // re-throw as an IllegalArgumentException
- throw new IllegalArgumentException("Passed a listener " +
- "that could not be serialized");
- }
- }
- } finally {
- store.releaseMutatorLock();
- }
- }
-
- /**
- * Handle failures to renew a lease by removing the lease from its set
- * and if needed schedule sending an event.
- * @param clw the wrapped client lease for the lease that could not
- * be renewed. <code>clw.isDeformed</code> must be
- * <code>false</code>.
- */
- void renewalFailure(ClientLeaseWrapper clw) {
- final LeaseSet set = clw.getLeaseSet();
- if (set == null) {
- // set must have just been removed, no state to update, no
- // events to send, just return
- return;
- }
-
- try {
- store.acquireMutatorLock();
-
- synchronized (set) {
- logger.log(Level.INFO, "Lease renewal failed for {0}", clw);
-
- if (!isCurrent(set)) {
- // expired, no state to update, no
- // events to send, just return
- return;
- }
-
- set.renewalFailure(clw);
-
- // Remove from LRM. Should already be
- // gone from LRM, but it might have been added back in
- // between the time the renewal failure occurred and
- // when we got around to processing it (that is now).
- // Making sure it is gone will keep things
- // consistent.
- try {
- lrm.remove(clw);
- } catch (UnknownLeaseException e) {
- // As long as the lease is gone we don't care
- }
- }
- } finally {
- store.releaseMutatorLock();
- }
- }
-
-
- /**
- * Remove a lease that has reached its desired expiration.
- * @param clw the wrapped client lease for the lease that we are done with
- */
- void desiredExpirationReached(ClientLeaseWrapper clw) {
- final LeaseSet set = clw.getLeaseSet();
- if (set == null) {
- // set must have just been removed, no state to update, no
- // events to send, just return
- return;
- }
-
- try {
- store.acquireMutatorLock();
-
- synchronized (set) {
- if (!isCurrent(set)) {
- // expired, no state to update, no events to send,
- // just return
- return;
- }
-
- // Make sure the lease is still in the set it thinks
- // it is
- if (!set.doesContainWrapper(clw)) {
- // Must have been removed somewhere else
- // forget about this event
- return;
- }
-
- // The client could have re-added the lease to the set
- // after the event occured but before we processed the
- // event. Check to make sure it really should be
- // removed.
- final long desiredExpiration = clw.getMembershipExpiration();
- if (desiredExpiration > System.currentTimeMillis()) {
- // Not dead yet...still in the rest of our tables,
- // just need to make sure it is in the LRM
- lrm.renewUntil(clw, clw.getMembershipExpiration(),
- clw.getRenewDuration(), lrmEventListener);
- return;
- }
-
- // They could have re-added the lease but in way that
- // it should generate a renewal failure event. Note if
- // we are here the lease must be pass its desired
- // expiration
- if (clw.getExpiration() < desiredExpiration) {
- // This time just return, the LRM will generate a
- // renewal failure event, or may have
- // already. Either way we don't need to add the
- // lease back and failure event will remove it
- // from the other tables
- return;
- }
-
- // If we are here the lease should be removed
- logger.log(Level.FINE,
- "Reached desired expiration for lease {0}",
- clw);
-
- set.remove(clw);
-
- // Remove from LRM. Should already be
- // gone from LRM, but it might have been added back in
- // between the time the renewal failure occurred and
- // when we got around to processing it (that is now).
- // Make sure it is gone will keep things
- // consistent.
- try {
- lrm.remove(clw);
- } catch (UnknownLeaseException e) {
- // As long as the lease is gone we don't care
- }
- }
- } finally {
- store.releaseMutatorLock();
- }
- }
-
- /**
- * The implementation of <code>SendMonitor</code> we use to track
- * event delivery threads. Each set gets its own object.
- */
- private class SendMonitorImpl implements SendMonitor {
- /** Set this is the monitor for */
- final private LeaseSet set;
-
- /**
- * Simple constructor.
- * @param set the set this monitor is associated with
- */
- private SendMonitorImpl(LeaseSet set) {
- this.set = set;
- }
-
- // Methods needed to meet contract of SendMonitor
-
- // Inherit java doc from super type
- public void definiteException(EventType type,
- RemoteEvent ev,
- long registrationNumber,
- Throwable t)
- {
- // This may be more locking than we need (especially
- // locking on the set) since EventType objects already
- // perform a lot of locking, but this discipline is
- // consistent with the rest of Norm and is therefore less
- // likely to lead to bugs down the road.
- try {
- store.acquireMutatorLock();
-
- if (!NormServerBaseImpl.isCurrent(set)) {
- // Set is dead, don't bother updating its state
- return;
- }
-
- synchronized (set) {
- set.definiteException(type, ev, registrationNumber);
- }
- } finally {
- store.releaseMutatorLock();
- }
- }
-
- // Inherit java doc from super type
- public boolean isCurrent() {
- synchronized (set) {
- return NormServerBaseImpl.isCurrent(set);
- }
- }
- }
-
-
- /**
- * Method used to remove membership expired leases from the server.
- * Assumes that they have already been removed from the set.
- * @param deadLeases an iterator with the leases that have to be
- * removed
- */
- private void removeClientLeases(Iterator deadLeases) {
- while(deadLeases.hasNext()) {
- ClientLeaseWrapper clw = (ClientLeaseWrapper) deadLeases.next();
-
- // Remove from lrm
- try {
- lrm.remove(clw);
- } catch (UnknownLeaseException e) {
- // This can happen if there was some problem renewing
- // the lease, or its LRM expiration just ran out.
- // Since we are remove the lease any way ignore.
- }
- }
- }
-
- /**
- * Throw a NoSuchObjectException, wrapped in a ThrowThis, if the
- * passed set has expired. Assumes set is locked.
- */
- private static void ensureCurrent(LeaseSet set) throws ThrowThis {
- if (!isCurrent(set)) {
- throw new ThrowThis(new NoSuchObjectException("Set has expired"));
- }
- }
-
- /**
- * Returns true if the lease on the lease set is still current, else
- * false.
- */
- private static boolean isCurrent(LeaseSet set) {
- return set.getExpiration() > System.currentTimeMillis();
- }
-
- /**
- * Return the set with the specified id, or throw a
- * NoSuchObjectException, wrapped in a ThrowThis if the set can't be found
- */
- private LeaseSet getSet(Uuid id) throws ThrowThis {
- final LeaseSet rslt = (LeaseSet) setTable.get(id);
- if (rslt == null)
- throw new ThrowThis(new NoSuchObjectException("Can't find set"));
- return rslt;
- }
-
-
- /////////////////////////////////////////
- // Methods defined in LeaseRenewalService
-
- // Inherit java doc from super type
- public LeaseRenewalSet createLeaseRenewalSet(long leaseDuration) {
- ready.check();
- final Uuid newID = UuidFactory.generate();
- final LeaseSet newSet = new LeaseSet(newID, generator, store, this);
-
- LeasePeriodPolicy.Result leasePeriod;
- try {
- leasePeriod = setLeasePolicy.grant(newSet, leaseDuration);
- } catch (LeaseDeniedException e) {
- // This will never happen because we never use a policy that
- // denies granting (or renewing) a lease; complain bitterly
- // and throw a runtime exception
- logger.log(Level.WARNING,
- "Got LeaseDeniedException creating lease -- " +
- "this should not happen!",
- e);
- throw new InternalNormException("Error creating lease", e);
- }
-
- Lease newLease = leaseFactory.newLease(newID, leasePeriod.expiration);
- newSet.setExpiration(leasePeriod.expiration);
- try {
- store.acquireMutatorLock();
- final Object u = new CreateLeaseSet(newSet);
- store.update(u);
- expMgr.register(newSet);
- setTable.put(newID, newSet);
- } finally {
- store.releaseMutatorLock();
- }
-
- LeaseRenewalSet result = SetProxy.create(serverProxy, newID, newLease);
- logger.log(Level.FINE, "Created lease renewal set {0}", result);
- return result;
- }
-
- //////////////////////////////
- // Callbacks used by LeaseSet
-
- /**
- * Method used by <code>LeaseSet</code> when it needs to cons up
- * a SetProxy with an up-to-date Lease. Assumes the appropriate
- * locks have been obtained.
- */
- SetProxy newSetProxy(LeaseSet set) {
- Lease l = leaseFactory.newLease(set.getUuid(), set.getExpiration());
- return SetProxy.create(serverProxy, set.getUuid(), l);
- }
-
- /**
- * Create a new <code>SendMonitorImpl</code>
- */
- // $$$ This feels a bit questionable, one of the places where this
- // is called back is in LeaseSet's constructor! So the constructor is
- // letting a reference to the constructed object escape before the object
- // is done. There are a couple alternatives but I am not sure they are
- // better, the best one is probably to defer creation of the set's
- // EventType object until first use and grab the necessary SendMonitor
- // then, but then we will need all sorts of null guards...
- SendMonitor newSendMonitor(LeaseSet set) {
- return new SendMonitorImpl(set);
- }
-
- //////////////////////////////////////////
- // Implement ServerProxyTrust
- /**
- * @throws UnsupportedOperationException if the server proxy does not
- * implement both {@link RemoteMethodControl} and {@link
- * TrustEquivalence}
- */
- public TrustVerifier getProxyVerifier() {
- return new ProxyVerifier(serverProxy, serverUuid);
- }
-
- //////////////////////////////////////////
- // Thread to persist client lease renewals
- static class RenewLogThread extends InterruptedStatusThread {
- private PersistentStore store;
- private final List renewedList;
- /** Create a daemon thread */
- RenewLogThread(List renewedList) {
- super("log renewals thread");
- setDaemon(true);
- this.renewedList = renewedList;
-
- }
-
- private void setStore(PersistentStore store){
- synchronized (this){
- if (this.store == null) this.store = store;
- }
- }
-
- public void run() {
- while (!hasBeenInterrupted()) {
- try {
- ClientLeaseWrapper clw;
- PersistentStore store;
- synchronized (this){
- store = this.store;
- }
- synchronized (renewedList) {
- // If there is an item on the list pull it off for
- // processing, otherwise wait and try again
- if (renewedList.isEmpty()) {
- try {
- renewedList.wait();
- continue; // go back to top of loop
- } catch (InterruptedException e) {
- // someone wants us dead
- // Reset the interrupt
- Thread.currentThread().interrupt();
- return;
- }
- } else {
- clw = (ClientLeaseWrapper) renewedList.remove(0);
- }
- }
-
- if (logger.isLoggable(Level.FINER)) {
- logger.log(Level.FINER,
- "Attempting to renew lease {0} at {1}",
- new Object[] {
- clw,
- Long.valueOf(System.currentTimeMillis()) });
- }
-
- // A lease was renewed, log the new state
- final LeaseSet set = clw.getLeaseSet();
- if (set == null) {
- // set must have just been removed, no state
- // to update, go to next item in list
- continue;
- }
-
- try {
- store.acquireMutatorLock();
- synchronized (set) {
- if (!isCurrent(set)) {
- // expired, no state to update, go to next item
- // in list
- continue;
- }
-
- clw.clearRenewed();
- // Small window here where the lease can
- // be renewed, it's renewed flag is re-set
- // and because it was cleared the clw ends
- // up on renewed list, we then come back
- // to this thread and log the new
- // state, and come back later and log that
- // state again. Since this is just
- // slightly wasteful, not incorrect, this
- // is ok. [Reversing these two lines of
- // course would be wrong...]
- set.logRenewal(clw);
- }
- } finally {
- store.releaseMutatorLock();
- }
- try {
- // Give other threads a chance to run
- Thread.sleep(100L);
- // Thread.yield();
- } catch (InterruptedException ex) {
- // Reset the interrupt status.
- Thread.currentThread().interrupt();
- }
- } catch (RuntimeException e) {
- logger.log(
- Level.INFO,
- "Runtime exception in RenewLogThread -- restarting",
- e);
- }
- }
- }
- }
-
-
- //////////////////////////////
- // Methods defined in Landlord
-
- // Inherit java doc from super type
- public long renew(Uuid cookie, long extension)
- throws UnknownLeaseException, LeaseDeniedException
- {
- ready.check();
- final LeaseSet set = (LeaseSet) setTable.get(cookie);
- if (set == null)
- throw new UnknownLeaseException("No lease for cookie:" + cookie);
- try {
- store.acquireMutatorLock();
-
- synchronized (set) {
- if (!isCurrent(set)) {
- // Lease has expired, don't renew
- throw new UnknownLeaseException(
- "Lease has already expired");
- }
- // No one can expire the lease, so it is safe to update.
- // $$$ Might be better to make an extra call to currentTime
- // and calculate the new duration just be for returning
- long oldExpiration = set.getExpiration();
- LeasePeriodPolicy.Result leasePeriod =
- setLeasePolicy.renew(set, extension);
-
- // Log update
- final Object u = new LeaseSet.ChangeSetExpiration(
- set, leasePeriod.expiration);
- store.update(u);
-
- set.setExpiration(leasePeriod.expiration);
- expMgr.reschedule(set);
- return leasePeriod.duration;
- }
- } finally {
- store.releaseMutatorLock();
- }
- }
-
- // Inherit java doc from super type
- public void cancel(Uuid cookie) throws UnknownLeaseException {
- ready.check();
- final LeaseSet set = (LeaseSet) setTable.get(cookie);
- if (set == null)
- throw new UnknownLeaseException("No lease for cookie:" + cookie);
-
- try {
- store.acquireMutatorLock();
-
- synchronized (set) {
- if (!isCurrent(set)) {
- //Someone else beat us to it, just return
- return;
- }
-
- removeSet(set);
- }
- } finally {
- store.releaseMutatorLock();
- }
- }
-
- /**
- * Do the heavy lifting on removing a set, assumes the locks on the
- * set and store have been acquired.
- */
- private void removeSet(LeaseSet set) {
- // handle the possibility of a race calling removeSet twice for a set
- if (setTable.remove(set.getUuid()) != null) {
- // set.destroy will persist the removal of the set, and
- // change the set's expiration to -1 which will cause any
- // other operations on the set to throw NoSuchObjectException
- // or UnknownLeaseException (they will all lock on the set
- // and ensure current before doing anything substantive)
- final Set leases = set.destroy();
- removeClientLeases(leases.iterator());
- }
- }
-
- // Inherit java doc from super type
- public RenewResults renewAll(Uuid[] cookies, long[] extensions) {
- ready.check();
- /* Cookie types checked in individual renew calls */
- return LandlordUtil.renewAll(this, cookies, extensions);
- }
-
- // Inherit java doc from super type
- public Map cancelAll(Uuid[] cookies) {
- ready.check();
- /* Cookie types checked in individual cancel calls */
- return LandlordUtil.cancelAll(this, cookies);
- }
-
- ////////////////////////////////////////////////
- // Methods and classes needed by PersistentStore
-
- /**
- * Called by <code>PersistentStore</code> after every update to give
- * server a chance to trigger a snapshot.
- * @param updateCount number of updates since last snapshot
- */
- void updatePerformed(int updateCount) {
- // First check to see if the size of the log is larger than a
- // minimum threshold, this keeps snapshots from happening when
- // the state of the server is very small (like initially when
- // the snapshot size is zero)
- if (updateCount >= logToSnapshotThresh) {
- // Compare the size of log to what next snapshot would be
- final int snapshotSize = setTable.size() + countLeases.getCount();
- if ((float) updateCount >= snapshotWt*((float) snapshotSize)) {
- // Both conditions meet, trigger snapshot
- snapshotter.takeSnapshot();
- }
- }
- }
-
- /**
- * Perform the 3rd stage of log recovery, restoring the various pieces of
- * transient state (populating the LRM, restoring various transient
- * fields).
- */
- void restoreTransientState() {
- final long now = System.currentTimeMillis();
-
- for (Iterator i = setTable.values().iterator(); i.hasNext(); ) {
-
- final LeaseSet set = (LeaseSet) i.next();
-
- // Has this set expired?
- if (now > set.getExpiration()) {
- // yes it has, remove it and move on
- i.remove();
- // This is all we have to do because none of the other
- // bookkeeping has been done yet for this set
- continue;
- }
-
- final Iterator leases = set.restoreTransientState(
- generator, store, this, recoveredListenerPreparer);
-
- // Go through all the leases in the set and add them to
- // the right tables.
- while (leases.hasNext()) {
- // We sync here so all the updates will be flushed to
- // memory before the the LRM's threads start working on
- // this lease
- ClientLeaseWrapper clw;
- synchronized (this) {
- clw = (ClientLeaseWrapper) leases.next();
-
- // Let the clw recover its state
- clw.recoverTransient(
- renewedList, idGen, set, recoveredLeasePreparer);
- }
-
- // Note: there is no race condition here because
- // lrmEventListener and renewedList will buffer any
- // renewals/renewal failure until the rest of the server
- // is ready to handle them
-
- // Note: We may be adding client leases who's
- // desired expirations were before `now'. However, this is
- // ok as the LRM will generate either a renewal failure
- // (if the actual expiration of the lease is before
- // the desired expiration) or a desired expiration
- // reached event (if the actual expiration of the lease
- // equal to or after the desired expiration), which in
- // turn will cause the "right things" to happen. Also
- // all the other methods are smart enough not be confused by
- // leases that have reached their desired expiration
- // but have not yet been removed.
-
- final Throwable lt = clw.getLastFailure();
-
- if ((lt == null) ||
- (ThrowableConstants.retryable(lt) ==
- ThrowableConstants.INDEFINITE))
- {
- lrm.renewUntil(clw, clw.getMembershipExpiration(),
- clw.getRenewDuration(), lrmEventListener);
- } else {
- // This lease has already suffered a definite failure,
- // don't let the LRM renew it. Note we will only get
- // here if we logged a renewal failure, but could not
- // log the LeaseRenewalEvent from the LRM
- lrmEventListener.notify(new LeaseRenewalEvent(lrm,
- clw, clw.getMembershipExpiration(), lt));
- }
- }
- }
- }
-
- /**
- * Implementation of <code>LogHandler</code> used by NormServerBaseImpl
- */
- private class OurLogHandler extends LogHandler {
- // Inherit java doc from super type
- // Snapshot format
- // Version
- // Server unique ID
- // Generator we use to create EventType objects
- // The number of LeaseSet objects in the setTable
- // All of the LeaseSet objects
- public void snapshot(OutputStream out) throws Exception {
- final long now = System.currentTimeMillis();
- final ObjectOutputStream oostream = new ObjectOutputStream(out);
-
- oostream.writeInt(CURRENT_LOG_VERSION);
- oostream.writeObject(serverUuid);
- oostream.writeObject(generator);
- oostream.writeInt(setTable.size());
- final Collection sets = setTable.values();
- for (Iterator i = sets.iterator(); i.hasNext();) {
- final LeaseSet set = (LeaseSet) i.next();
-
- // Grab lock on set so any concurrent getLeases() calls
- // will not be corrupted
- synchronized (set) {
- // Note, we used to drop desired expiration reached
- // client leases here, but that was a bug. In particular
- // there could have been a renewal failure event
- // that we have not yet been notified of
- oostream.writeObject(set);
- }
- }
- oostream.flush();
-
- // $$$ We are missing an optimization here: as we write each
- // set we could check to see if any of the client leases
- // in that set have been renewed, but not logged, and clear
- // them from the renewedList (this assumes that the
- // renewal changes the persisted state of the wrapper, not
- // the processing by logRenewal()). This is not a bug
- // since any clw we pull off the renewedList and persist
- // after this point will have state at least as up-to-date
- // as this snapshot.
- }
-
- // Inherit java doc from super type
- public void recover(InputStream in) throws Exception {
- final ObjectInputStream oistream = new ObjectInputStream(in);
-
- int version;
- version = oistream.readInt();
-
- if (version != CURRENT_LOG_VERSION) {
- throw new CorruptedStoreException("Incompatible version " +
- "ID in log, looking for " + CURRENT_LOG_VERSION +
- ", got " + version);
-
- }
-
- serverUuid = (Uuid) oistream.readObject();
- generator = (EventTypeGenerator) oistream.readObject();
- final int size = oistream.readInt();
- setTable.clear();
- for (int i = 0; i < size; i++) {
- final LeaseSet set = (LeaseSet) oistream.readObject();
- setTable.put(set.getUuid(), set);
- }
- }
-
- // Inherit java doc from super type
- public void applyUpdate(Object update) throws Exception {
- final LoggedOperation op = (LoggedOperation) update;
- op.apply(setTable);
- }
- }
-
- /**
- * Return a string summarizing the inventory of the server
- */
- private String inventory() {
- return countLeases.getCount() + " client leases, " +
- setTable.size() + " sets.";
- }
-
- /**
- * Thread that performs the actual snapshots, done in a separate thread
- * so it will not hang up in-progress remote calls
- */
- class SnapshotThread extends InterruptedStatusThread {
- private final PersistentStore store;
- /** Create a daemon thread */
- SnapshotThread(PersistentStore store) {
- super("snapshot thread");
- setDaemon(true);
- this.store = store;
- }
-
- /** Signal this thread that is should take a snapshot */
- private synchronized void takeSnapshot() {
- notifyAll();
- }
-
- public void run() {
- while (!hasBeenInterrupted()) {
- synchronized (this) {
- try {
- wait();
- } catch (InterruptedException e) {
- return;
- }
- }
-
- try {
- if (logger.isLoggable(Level.FINER)) {
- logger.log(
- Level.FINER, "Taking snapshot: {0}", inventory());
- }
-
- store.snapshot();
-
- } catch (InterruptedIOException e) {
- // Some one wants us dead
- return;
- } catch (Exception e) {
- if (e instanceof LogException &&
- ((LogException) e).detail instanceof
- InterruptedIOException)
- {
- return;
- }
- /* $$$
- * if taking the snapshot fails for any reason,
- * then one of the following must be done:
- * -- output the problem to a file and exit
- * -- output the problem to a file and continue
- * -- set an "I have a problem" attribute and
- * then send a notification
- * this issue will be addressed at a later time
- */
- logger.log(Level.WARNING, "Snapshot failed", e);
- }
- }
- }
- }
-
- ///////////////////////////////////
- // Methods defined in Administrable
-
- // Inherit java doc from super type
- public Object getAdmin() {
- ready.check();
- return adminProxy;
- }
-
- ///////////////////////////////
- // Methods defined in JoinAdmin
-
- // Inherit java doc from super type
- public Entry[] getLookupAttributes() {
- ready.check();
- return joinState.getAttributes();
- }
-
- // Inherit java doc from super type
- public void addLookupAttributes(Entry[] attrSets) {
- ready.check();
- joinState.addAttributes(attrSets, true);
- logger.log(Level.CONFIG, "Added attributes");
- }
-
- // Inherit java doc from super type
- public void modifyLookupAttributes(Entry[] attrSetTemplates,
- Entry[] attrSets)
- {
- ready.check();
- joinState.modifyAttributes(attrSetTemplates, attrSets, true);
- logger.log(Level.CONFIG, "Modified attributes");
- }
-
- // Inherit java doc from super type
- public String[] getLookupGroups() {
- ready.check();
- return joinState.getGroups();
- }
-
- // Inherit java doc from super type
- public void addLookupGroups(String[] groups) {
- ready.check();
- joinState.addGroups(groups);
- if (logger.isLoggable(Level.CONFIG)) {
- logger.log(Level.CONFIG, "Added lookup groups: {0}",
- toString(groups));
- }
- }
-
- // Inherit java doc from super type
- public void removeLookupGroups(String[] groups) {
- ready.check();
- joinState.removeGroups(groups);
- if (logger.isLoggable(Level.CONFIG)) {
- logger.log(Level.CONFIG, "Removed lookup groups: {0}",
- toString(groups));
- }
- }
-
- // Inherit java doc from super type
- public void setLookupGroups(String[] groups) {
- ready.check();
- joinState.setGroups(groups);
- if (logger.isLoggable(Level.CONFIG)) {
- logger.log(Level.CONFIG, "Set lookup groups: {0}",
- toString(groups));
- }
- }
-
- // Inherit java doc from super type
- public LookupLocator[] getLookupLocators() {
- ready.check();
- return joinState.getLocators();
- }
-
- // Inherit java doc from super type
- public void addLookupLocators(LookupLocator[] locators)
- throws RemoteException
- {
- ready.check();
- for (int i = locators.length; --i >= 0; ) {
- locators[i] = (LookupLocator) locatorPreparer.prepareProxy(
- locators[i]);
- }
- joinState.addLocators(locators);
- if (logger.isLoggable(Level.CONFIG)) {
- logger.log(Level.CONFIG, "Added lookup locators: {0}",
- toString(locators));
- }
- }
-
- // Inherit java doc from super type
- public void removeLookupLocators(LookupLocator[] locators)
- throws RemoteException
- {
- ready.check();
- for (int i = locators.length; --i >= 0; ) {
- locators[i] = (LookupLocator) locatorPreparer.prepareProxy(
- locators[i]);
- }
- joinState.removeLocators(locators);
- if (logger.isLoggable(Level.CONFIG)) {
- logger.log(Level.CONFIG, "Removed lookup locators: {0}",
- toString(locators));
- }
- }
-
- // Inherit java doc from super type
- public void setLookupLocators(LookupLocator[] locators)
- throws RemoteException
- {
- ready.check();
- for (int i = locators.length; --i >= 0; ) {
- locators[i] = (LookupLocator) locatorPreparer.prepareProxy(
- locators[i]);
- }
- joinState.setLocators(locators);
- if (logger.isLoggable(Level.CONFIG)) {
- logger.log(Level.CONFIG, "Set lookup locators: {0}",
- toString(locators));
- }
- }
-
- /** Returns the contents of an array as a string. */
- private static String toString(Object[] array) {
- if (array == null) {
- return "null";
- }
- StringBuffer sb = new StringBuffer(String.valueOf(array[0]));
- for (int i = 1; i < array.length; i++) {
- sb.append(", ").append(array[i]);
- }
- return sb.toString();
- }
-
- //////////////////////////////////
- // Methods defined in DestroyAdmin
-
- // Inherit java doc from super type
- public void destroy() throws RemoteException {
- ready.shutdown();
- logger.log(Level.INFO, "Destroying Norm service");
-
- joinState.terminateJoin();
- lrmEventListener.interrupt();
- renewLogger.interrupt();
- snapshotter.interrupt();
- expMgr.terminate();
- generator.terminate();
- lrm.clear();
- lrm.close();
-
- logger.log(Level.FINEST, "Independent threads interrupted");
-
- new DestroyThread().start();
- logger.log(Level.FINEST, "Destroy thread started");
- }
-
- /**
- * Unexport our stub appropriately.
- * @param force terminate in progress calls if necessary
- * @return true if unexport succeeds
- */
- boolean unexport(boolean force) throws NoSuchObjectException {
- return exporter.unexport(force);
- }
-
- /**
- * Method subclasses can override to perform any necessary post
- * log destruction cleanup.
- */
- void postDestroy() {
- }
-
- /**
- * Termination thread code. We do this in a separate thread to
- * avoid deadlock, because Activatable.inactive will block until
- * in-progress RMI calls are finished.
- */
- private class DestroyThread extends Thread {
- /** Maximum delay for unexport attempts */
- private static final long MAX_DELAY = 2 * 60 * 1000;
-
- /** Create a non-daemon thread */
- private DestroyThread() {
- super("DestroyThread");
- /* override inheritance from RMI daemon thread */
- setDaemon(false);
- }
-
- public void run() {
- logger.log(Level.FINEST, "DestroyThread running");
-
- /*
- * Work for up to MAX_DELAY to try to nicely
- * unexport our stub, but if that does not work just end
- */
- final long end_time = System.currentTimeMillis() + MAX_DELAY;
- boolean unexported = false;
-
- try {
- while ((!unexported) &&
- (System.currentTimeMillis() < end_time))
- {
- /* wait for any pending operations to complete */
- logger.log(Level.FINEST,
- "Calling unexport (force=false)...");
-
- unexported = unexport(false);
-
- logger.log(Level.FINEST, "...rslt = " + unexported);
-
- if (!unexported) {
- // Thread.yield();
- try {
- Thread.sleep(100L);
- } catch (InterruptedException e){
- // Reset interrupt status
- Thread.currentThread().interrupt();
- }
- }
- }
- } catch (NoSuchObjectException e) {
- logger.log(Level.FINEST, "...rslt = NoSuchObjectException");
-
- unexported = true; // This works too
- } catch (Throwable t) {
- logger.log(Level.FINEST, "...rslt = ", t);
- }
-
- if (!unexported) {
- /* Attempt to forcefully export the service */
- try {
- logger.log(Level.FINEST, "Calling unexport (force=true)");
-
- unexport(true);
- } catch (NoSuchObjectException e) {
- // This works too
- }
- }
-
- // Try to join the independent threads before deleting the store
- try {
- logger.log(Level.FINEST, "Joining independent threads");
-
- lrmEventListener.join(MAX_DELAY);
- renewLogger.join(MAX_DELAY);
- snapshotter.join(MAX_DELAY);
- } catch (InterruptedException e) {
- // Will not happen
- }
-
- try {
- logger.log(Level.FINEST, "Destroying store");
-
- store.destroy();
- } catch (Exception t) {
- logger.log(Level.INFO,
- "While destroying persistent store -- " +
- "destroy continuing",
- t);
- }
-
- if (lifeCycle != null) {
- /* Unregister the service implementation */
- lifeCycle.unregister(this);
- }
-
- logger.log(Level.FINEST, "Calling postDestroy");
-
- postDestroy();
-
- if (loginContext != null) {
- try {
- logger.log(Level.FINEST, "Logging out");
- loginContext.logout();
- } catch (Exception e) {
- logger.log(
- Level.INFO, "Exception while logging out", e);
- }
- }
-
- logger.log(Level.FINEST, "Ending DestroyThread");
- }
- }
-
- /* -- Implement ServiceProxyAccessor -- */
-
- /** {@inheritDoc} */
- public Object getServiceProxy() {
- ready.check();
- return normProxy;
- }
-
- /* -- Implement ProxyAccessor -- */
-
- /** {@inheritDoc} */
- public Object getProxy() {
- /* Don't wait until ready to return the server proxy */
- return serverProxy;
- }
-
- ////////////////////
- // Server setup code
-
- /** Returns a string representation of this object. */
- public String toString() {
- String className = getClass().getName();
- className = className.substring(className.lastIndexOf('.') + 1);
- return className + "[" + serverUuid + "]";
- }
-
- /**
- * Simple container for an alternative return a value so we
- * can provide more detailed diagnostics.
- */
- static class InitException extends Exception {
- private static final long serialVersionUID = 1;
- private InitException(String message, Throwable nested) {
- super(message, nested);
- }
- }
-
- /**
- * Portion of construction that is common between the activatable and not
- * activatable cases. This method performs the minimum number of
- * operations before establishing the Subject, and logs errors.
- */
- static NormServerInitializer init(String[] configOptions, final NormServerInitializer init)
- throws Exception
- {
- try {
- final Configuration config = ConfigurationProvider.getInstance(
- configOptions, NormServerBaseImpl.class.getClassLoader());
- init.loginContext = (LoginContext) config.getEntry(
- NORM, "loginContext", LoginContext.class, null);
- if (init.loginContext == null) {
- init.initAsSubject(config);
- } else {
- init.loginContext.login();
- try {
- Subject.doAsPrivileged(
- init.loginContext.getSubject(),
- new PrivilegedExceptionAction() {
- public Object run() throws Exception {
- init.initAsSubject(config);
- return null;
- }
- },
- null);
- } catch (PrivilegedActionException e) {
- throw e.getCause();
- }
- }
- } catch (Throwable e) {
- initFailed(e);
- }
- return init;
- }
-
- /**
- * Log information about failing to initialize the service and rethrow the
- * appropriate exception.
- *
- * @param e the exception produced by the failure
- */
- static void initFailed(Throwable e) throws Exception {
- String message = null;
- if (e instanceof InitException) {
- message = e.getMessage();
- e = e.getCause();
- }
- if (logger.isLoggable(Level.SEVERE)) {
- if (message != null) {
- logThrow(Level.SEVERE, "initFailed",
- "Unable to start Norm service: {0}",
- new Object[] { message }, e);
- } else {
- logger.log(Level.SEVERE, "Unable to start Norm service", e);
- }
- }
- if (e instanceof Exception) {
- throw (Exception) e;
- } else if (e instanceof Error) {
- throw (Error) e;
- } else {
- throw new IllegalStateException(e);
- }
- }
-
- /** Logs a throw */
- private static void logThrow(Level level, String method,
- String msg, Object[] msgParams, Throwable t)
- {
- LogRecord r = new LogRecord(level, msg);
- r.setLoggerName(logger.getName());
- r.setSourceClassName(NormServerBaseImpl.class.getName());
- r.setSourceMethodName(method);
- r.setParameters(msgParams);
- r.setThrown(t);
- logger.log(r);
- }
-
- public void start() throws Exception {
- synchronized (this){
- if (started) return;
- started = true;
- }
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction(){
-
- @Override
- public Object run() throws Exception {
- serverProxy = (NormServer) exporter.export(NormServerBaseImpl.this);
-
- boolean done = false;
- try {
- // We use some of these during the recovery process
- expMgr.setServer(NormServerBaseImpl.this);
- lrmEventListener.setServer(NormServerBaseImpl.this);
-
- try {
- store = new PersistentStore(
- persistenceDirectory, new OurLogHandler(), NormServerBaseImpl.this);
- // Creating the store completes the first two stages of
- // log recovery (reading the snapshot and the updates)
- // Perform the last stage here of restoring transient state
- restoreTransientState();
- if (logger.isLoggable(Level.FINER)) {
- logger.log(Level.FINER, "Log recovered: {0}",
- inventory());
- }
-
- } catch (CorruptedStoreException e) {
- throw new InitException("Log corrupted, can't recover ", e);
- } catch (StoreException e) {
- throw new InitException("Can't recover log", e);
- }
-
- renewLogger.setStore(store);
- snapshotter = new SnapshotThread(store);
-
- if (serverUuid == null) {
- serverUuid = UuidFactory.generate();
- }
- normProxy = NormProxy.create(serverProxy, serverUuid);
- adminProxy = AdminProxy.create(serverProxy, serverUuid);
-
- // Create new baseline snapshot
- try {
- store.snapshot();
- if (logger.isLoggable(Level.FINER)) {
- logger.log(
- Level.FINER, "Completed new baseline snapshot: {0}",
- inventory());
- }
- } catch (IOException e) {
- throw new InitException(
- "Can't create new baseline snapshot", e);
- }
-
- Entry[] serviceAttributes = {
- new ServiceInfo(
- "Lease Renewal Service", // name
- "Sun Microsystems, Inc.", // manufacturer
- "Sun Microsystems, Inc.", // vender
- VersionConstants.SERVER_VERSION, // version
- "", // model
- ""), // serialNumber
- new BasicServiceType("Lease Renewal Service")
- };
- try {
- JoinState joinState = new JoinState(
- normProxy, lrm, config, serviceAttributes,
- recoveredLocatorPreparer,
- new ServiceID(serverUuid.getMostSignificantBits(),
- serverUuid.getLeastSignificantBits()));
- store.addSubStore(joinState);
- // joinState is mutated when added to store, makes
- // sure that's published after mutation.
- NormServerBaseImpl.this.joinState = joinState;
- } catch (StoreException e) {
- throw new InitException("Can't create JoinState", e);
- }
-
- leaseFactory = new LeaseFactory(serverProxy, serverUuid);
-
- // $$$ By rights this code should be in
- // restoreTransientState(), however we can't have an independent
- // thread running around changing persistant state util we get
- // to this point (I think the only real issue is the baseline
- // snapshot) and once we place a set in the expMgr it the
- // underlying wakeup queue will start running which can cause
- // calls to expireIfTime() (and sendWarningEvent() though those
- // should not be a problem since they don't log anything).
- //
- // I would prefer to ether modify WakeupQueue() to have a "start
- // now" method (equivalent to how we create lrmEventListener above
- // but call start() bellow) or be able to hold an exclusive
- // snapshot lock until the initial snapshot is done. Ether should
- // allow this code to be moved into restoreTransientState
-
- // Move to Starter method, is WakeupQueue() above
- // actually WakeupManager() renamed? I suspect so.
- expMgr.start();
- for (Iterator i = setTable.values().iterator(); i.hasNext(); ) {
- final LeaseSet set = (LeaseSet) i.next();
- synchronized (set) {
- expMgr.schedule(set);
- }
- }
-
- lrmEventListener.start();
- renewLogger.start();
- snapshotter.start();
- done = true;
- } finally {
- if (!done) {
- try {
- unexport(true);
- } catch (Exception e) {
- logger.log(
- Level.INFO,
- "Unable to unexport after failure during startup",
- e);
- }
- }
- }
- return null;
- }
-
- }, context);
- ready.ready();
- logger.log(Level.INFO, "Norm service started: {0}", this);
- } catch (PrivilegedActionException e){
- initFailed(e.getException());
- } catch (Throwable e) {
- initFailed(e);
- } finally {
- context = null;
- config = null;
- }
- }
-
- /** Returns whether to isolate renewal sets or batch lease across sets. */
- boolean isolateSets() {
- return isolateSets;
- }
-
- /**
- * Creates an instance of this class.
- */
- NormServerBaseImpl(NormServerInitializer init){
- persistent = init.persistent;
- lifeCycle = init.lifeCycle;
- loginContext = init.loginContext;
- persistenceDirectory = init.persistenceDirectory;
- renewedList = init.renewedList;
- snapshotWt = init.snapshotWt;
- logToSnapshotThresh = init.logToSnapshotThresh;
- leasePreparer = init.leasePreparer;
- listenerPreparer = init.listenerPreparer;
- locatorPreparer = init.locatorPreparer;
- recoveredLeasePreparer = init.recoveredLeasePreparer;
- recoveredListenerPreparer = init.recoveredListenerPreparer;
- recoveredLocatorPreparer = init.recoveredLocatorPreparer;
- setLeasePolicy = init.setLeasePolicy;
- isolateSets = init.isolateSets;
- lrm = init.lrm;
- exporter = init.exporter;
- expMgr = init.expMgr;
- generator = init.generator;
- lrmEventListener = init.lrmEventListener;
- renewLogger = init.renewLogger;
- context = init.context;
- config = init.config;
- }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.sun.jini.norm;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.rmi.MarshalledObject;
+import java.rmi.NoSuchObjectException;
+import java.rmi.RemoteException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+
+import net.jini.config.Configuration;
+import net.jini.config.ConfigurationProvider;
+import net.jini.core.constraint.RemoteMethodControl;
+import net.jini.core.discovery.LookupLocator;
+import net.jini.core.entry.Entry;
+import net.jini.core.event.EventRegistration;
+import net.jini.core.event.RemoteEvent;
+import net.jini.core.event.RemoteEventListener;
+import net.jini.core.lease.Lease;
+import net.jini.core.lease.LeaseDeniedException;
+import net.jini.core.lease.UnknownLeaseException;
+import net.jini.core.lookup.ServiceID;
+import net.jini.export.Exporter;
+import net.jini.export.ProxyAccessor;
+import net.jini.id.ReferentUuid;
+import net.jini.id.Uuid;
+import net.jini.id.UuidFactory;
+import net.jini.lease.LeaseRenewalEvent;
+import net.jini.lease.LeaseRenewalManager;
+import net.jini.lease.LeaseRenewalService;
+import net.jini.lease.LeaseRenewalSet;
+import net.jini.lookup.entry.ServiceInfo;
+import net.jini.security.ProxyPreparer;
+import net.jini.security.TrustVerifier;
+import net.jini.security.proxytrust.ServerProxyTrust;
+import net.jini.security.proxytrust.TrustEquivalence;
+
+import com.sun.jini.constants.ThrowableConstants;
+import com.sun.jini.constants.VersionConstants;
+import com.sun.jini.landlord.Landlord.RenewResults;
+import com.sun.jini.landlord.LandlordUtil;
+import com.sun.jini.landlord.LeaseFactory;
+import com.sun.jini.landlord.LeasePeriodPolicy;
+import com.sun.jini.landlord.LocalLandlord;
+import com.sun.jini.lookup.entry.BasicServiceType;
+import com.sun.jini.norm.event.EventType;
+import com.sun.jini.norm.event.EventTypeGenerator;
+import com.sun.jini.norm.event.SendMonitor;
+import com.sun.jini.norm.lookup.JoinState;
+import com.sun.jini.proxy.ThrowThis;
+import com.sun.jini.start.LifeCycle;
+import com.sun.jini.reliableLog.LogException;
+import com.sun.jini.reliableLog.LogHandler;
+import org.apache.river.api.util.Startable;
+import com.sun.jini.thread.InterruptedStatusThread;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import net.jini.loader.ClassLoading;
+
+/**
+ * Base class for implementations of NormServer. Provides a complete
+ * non-activatable (but still logging) implementation.
+ *
+ * @author Sun Microsystems, Inc.
+ */
+abstract class NormServerBaseImpl
+ implements NormServer, LocalLandlord, ServerProxyTrust, ProxyAccessor, Startable
+{
+ /** Current version of log format */
+ private static final int CURRENT_LOG_VERSION = 2;
+
+ /** Logger and configuration component name for Norm */
+ static final String NORM = "com.sun.jini.norm";
+
+ /** Logger for logging information about this instance */
+ static final Logger logger = Logger.getLogger(NORM);
+
+ /** Whether this server is persistent. */
+ final boolean persistent;
+
+ /** The login context, for logging out */
+ final LoginContext loginContext;
+
+ /** The location of our persistent storage, or null if not persistent. */
+ final String persistenceDirectory;
+
+ /** Proxy preparer for leases supplied through the API */
+ final private ProxyPreparer leasePreparer;
+
+ /**
+ * Proxy preparer for leases recovered from persistent storage, or null if
+ * not persistent.
+ */
+ final private ProxyPreparer recoveredLeasePreparer;
+
+ /** Proxy preparer for listeners supplied through the API */
+ final private ProxyPreparer listenerPreparer;
+
+ /**
+ * Proxy preparer for listeners recovered from persistent storage, or null
+ * if not persistent.
+ */
+ final private ProxyPreparer recoveredListenerPreparer;
+
+ /**
+ * Proxy preparer for lookup locators supplied through the API, and not
+ * including initial lookup locators.
+ */
+ final private ProxyPreparer locatorPreparer;
+
+ /**
+ * Proxy preparer for lookup locators recovered from persistent storage, or
+ * null if not persistent.
+ */
+ final private ProxyPreparer recoveredLocatorPreparer;
+
+ /** The exporter for exporting and unexporting */
+ final Exporter exporter;
+
+ /** Object to notify when this service is destroyed, or null */
+ final private LifeCycle lifeCycle;
+
+ /** The unique ID for this server. */
+ volatile private Uuid serverUuid;
+
+ /** Our JoinManager */
+ volatile private JoinState joinState;
+
+ /** Map of Uuids to LeaseSets */
+ final private Map<Uuid,LeaseSet> setTable = Collections.synchronizedMap(new HashMap<Uuid,LeaseSet>());
+
+ /** Lease renewal manager that actually renews the leases */
+ final private LeaseRenewalManager lrm;
+
+ /** Object that expires sets and generates expiration warning events */
+ final private LeaseExpirationMgr expMgr;
+
+ /** Factory for creating leases */
+ volatile private LeaseFactory leaseFactory;
+
+ /** Policy we use for granting and renewing renewal set leases */
+ final private LeasePeriodPolicy setLeasePolicy;
+
+ /**
+ * Whether to isolate leases in their own renewal sets as opposed to
+ * batching leases across sets.
+ */
+ final private boolean isolateSets;
+
+ /** Our persistant store */
+ volatile private PersistentStore store;
+
+ /** Factory we use to create ClientLeaseWrapper IDs */
+ final private UIDGenerator idGen = new UIDGenerator() ;
+
+ /** List of leases that have been renewed but not persisted */
+ final private List renewedList;
+
+ /**
+ * Thread that pulls wrapped client leases off the renewedList and logs
+ * them to disk
+ */
+ final private RenewLogThread renewLogger;
+
+ /** Object used to generate new event types */
+ volatile private EventTypeGenerator generator;
+
+ /**
+ * Object that transfers events from the renewal manager to
+ * us so we can remove dead leases and send events
+ */
+ final private LRMEventListener lrmEventListener;
+
+ /** Log file must contain this many records before snapshot allowed */
+ final private int logToSnapshotThresh;
+
+ /** Weight factor applied to snapshotSize when deciding to take snapshot */
+ final private float snapshotWt;
+
+ /** Inner server proxy */
+ volatile NormServer serverProxy = null;
+
+ /** Outer service proxy */
+ volatile LeaseRenewalService normProxy = null;
+
+ /** Admin proxy */
+ volatile private AdminProxy adminProxy;
+
+ /** Thread that performs snapshots when signaled */
+ volatile private SnapshotThread snapshotter;
+
+ /** Lock protecting startup and shutdown */
+ private final ReadyState ready = new ReadyState();
+
+ /** Keep track of the number of leases. */
+ private final CountLeases countLeases = new CountLeases();
+
+ /** Fields access only by thread that called constructor, then called start()
+ * set to null at completion of start() */
+ private AccessControlContext context;
+ private Configuration config;
+
+ private boolean started;
+
+ ////////////////////////////////
+ // Methods defined in NormServer
+
+ // Inherit java doc from super type
+ public void renewFor(Uuid id, Lease leaseToRenew,
+ long membershipDuration, long renewDuration)
+ throws RemoteException, ThrowThis
+ {
+ ready.check();
+
+ // Lookup the set
+ final LeaseSet set = getSet(id);
+
+ if (leaseToRenew == null) {
+ throw new NullPointerException("LeaseRenewalSet.renewFor:Must " +
+ "pass a non-null lease");
+ }
+
+ if ((membershipDuration != Lease.FOREVER) &&
+ (renewDuration == Lease.ANY))
+ {
+ throw new IllegalArgumentException(
+ "LeaseRenewalSet.renewFor:renewDuration can only be " +
+ "Lease.ANY if membershipDuration is Lease.FOREVER");
+ }
+
+ if (!(renewDuration == Lease.ANY || renewDuration == Lease.FOREVER ||
+ renewDuration > 0))
+ {
+ throw new IllegalArgumentException(
+ "LeaseRenewalSet.renewFor:renewDuration can only be " +
+ "Lease.ANY, Lease.FOREVER, or positive");
+ }
+
+ leaseToRenew = (Lease) leasePreparer.prepareProxy(leaseToRenew);
+
+ // Ensure that the lease is not a current lease granted by this server
+ if (leaseToRenew instanceof ReferentUuid) {
+ Uuid cookie = ((ReferentUuid) leaseToRenew).getReferentUuid();
+ LeaseSet setForLease = (LeaseSet) setTable.get(cookie);
+ if (setForLease != null) {
+ synchronized (setForLease) {
+ if (isCurrent(setForLease)) {
+ throw new IllegalArgumentException(
+ "Cannot add leases granted by a " +
+ "LeaseRenewalService to a set created by " +
+ "that service");
+ }
+ }
+ }
+ }
+
+ // If we are told to dump the codebase of the lease we are adding
+ if (logger.isLoggable(Level.FINE)) {
+ final Class lc = leaseToRenew.getClass();
+ logger.log(Level.FINE,
+ "Adding lease of class {0} with annotation {1}",
+ new Object[] {
+ leaseToRenew.getClass(),
+ ClassLoading.getClassAnnotation(lc) });
+ }
+
+ // Add the lease to the set
+ add(set, leaseToRenew, membershipDuration, renewDuration);
+ }
+
+ /**
+ * Prevents access to the service before it is ready or after it starts to
+ * shutdown. Each public entrypoint to the service should call check or
+ * shutdown, and initialization should call ready when the service is ready
+ * to use.
+ */
+ private static final class ReadyState {
+ private static final int INITIALIZE = 0;
+ private static final int READY = 1;
+ private static final int SHUTDOWN = 2;
+ private int state = INITIALIZE;
+
+ /**
+ * Checks if the service is ready to use, waiting if it is
+ * initializing, and throwing IllegalStateException if it is shutting
+ * down.
+ */
+ synchronized void check() {
+ while (true) {
+ switch (state) {
+ case INITIALIZE:
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ break;
+ case READY:
+ return;
+ default:
+ throw new IllegalStateException(
+ "Norm service is unavailable");
+ }
+ }
+ }
+
+ /**
+ * Marks the service ready for use, throwing IllegalStateException if
+ * it is shutting down.
+ */
+ synchronized void ready() {
+ switch (state) {
+ case INITIALIZE:
+ state = READY;
+ notifyAll();
+ break;
+ case READY:
+ break;
+ default:
+ throw new IllegalStateException("Norm service is unavailable");
+ }
+ }
+
+ /**
+ * Marks the service as shutting down, waiting if it is initializing,
+ * and throwing IllegalStateException if it is already shutting down.
+ */
+ synchronized void shutdown() {
+ check();
+ state = SHUTDOWN;
+ notifyAll();
+ }
+ }
+
+ /** Keeps track of the number of leases. */
+ private static class CountLeases {
+ private int count;
+
+ private synchronized void updateCount(int change) {
+ count += change;
+ assert count >= 0;
+ }
+
+ private synchronized int getCount() {
+ return count;
+ }
+ }
+
+ /** Update the number of leases being managed by this server. */
+ void updateLeaseCount(int change) {
+ countLeases.updateCount(change);
+ }
+
+ /**
+ * Add the lease to the set.
+ *
+ * @param set the LeaseSet to add the leaseToRenew to
+ * @param leaseToRenew the lease the client wants managed
+ * @param membershipDuration the length of time the client
+ * wants the lease managed for
+ * @param renewDuration the length of time the client want the
+ * lease renewed for each time it is renewed
+ * @throws ThrowThis if the set no longer exists
+ */
+ private void add(LeaseSet set, Lease leaseToRenew,
+ long membershipDuration, long renewDuration)
+ throws ThrowThis
+ {
+ try {
+ store.acquireMutatorLock();
+ synchronized (set) {
+ ensureCurrent(set);
+
+ // Get a wrapper for the lease
+ final long now = System.currentTimeMillis();
+ ClientLeaseWrapper clw =
+ set.getClientLeaseWrapper(leaseToRenew);
+ if (clw == null) {
+ // We don't know about this lease, create a new wrapper
+ try {
+ clw = new ClientLeaseWrapper(
+ leaseToRenew, idGen.newID(), renewedList, set,
+ membershipDuration, renewDuration, now);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(
+ "NormServerBaseImpl.renewFor:Handed lease " +
+ "that can't be marshalled");
+ }
+ } else {
+ // We know about this lease -- update its stats
+ clw.update(membershipDuration, renewDuration, now);
+ }
+
+ set.update(clw);
+ lrm.renewUntil(clw, clw.getMembershipExpiration(),
+ clw.getRenewDuration(), lrmEventListener);
+
+ // $$$ What if this lease was just dropped by the LRM
+ // (say because there was some problem renewing the
+ // lease)? Are we going to lose the lease because
+ // we will shortly process a renewalFailureEvent?
+ // Is it a problem if we do? Presumably re-adding it
+ // is not going to fix the underlying problem...
+ }
+ } finally {
+ store.releaseMutatorLock();
+ }
+ }
+
+
+ // Inherit java doc from super type
+ public Lease remove(Uuid id, Lease leaseToRemove)
+ throws RemoteException, ThrowThis
+ {
+ ready.check();
+ final LeaseSet set = getSet(id);
+
+ if (leaseToRemove == null) {
+ throw new NullPointerException("LeaseRenewalSet.remove:Must " +
+ "pass a non-null lease");
+ }
+
+ leaseToRemove = (Lease) leasePreparer.prepareProxy(leaseToRemove);
+ logger.log(Level.FINE, "Removing lease {0}", leaseToRemove);
+
+ // The most up-to-date ref to the lease we have
+ Lease rslt = null;
+ try {
+ store.acquireMutatorLock();
+ synchronized (set) {
+ ensureCurrent(set);
+
+ final ClientLeaseWrapper clw
+ = set.getClientLeaseWrapper(leaseToRemove);
+
+ if (clw == null) {
+ // Lease must have been removed already
+ return null;
+ }
+
+ try {
+ lrm.remove(clw);
+ } catch (UnknownLeaseException e) {
+ // This can happen if there was some problem
+ // renewing the lease or its LRM expiration just
+ // ran out. Since we are removing the lease anyway
+ // ignore.
+ }
+
+ final boolean present = set.remove(clw);
+
+ // Only return a non-null result if the removed lease
+ // had not be removed already
+ if (present) {
+ // At this point we can assume clw is no
+ // longer deformed
+ rslt = clw.getClientLease();
+ }
+ }
+ } finally {
+ store.releaseMutatorLock();
+ }
+
+ if (rslt == null)
+ return null;
+
+ // Whenever we serialize a lease we have to make sure that
+ // its serial form will stay the same during the
+ // serialization. Since we have removed it from the set we
+ // don't have to worry about this lease being serialized to disk
+ // any more so changing the serial format here should be safe.
+ rslt.setSerialFormat(Lease.DURATION);
+ return rslt;
+ }
+
+ // Inherit java doc from super type
+ public GetLeasesResult getLeases(Uuid id) throws ThrowThis {
+ ready.check();
+ final LeaseSet set = getSet(id);
+
+ // We are not modifying the set so we don't need the mutator lock
+ // $$$ Do we need a reader lock, or is the lock on the set enough?
+ // $$$ Need to make sure we really don't mutate any persistent state
+ // or have issues with serializing the leases.
+ synchronized (set) {
+ ensureCurrent(set);
+ return new GetLeasesResult(set.getLeases());
+ }
+ }
+
+
+ // Inherit java doc from super type
+ public EventRegistration setExpirationWarningListener(
+ Uuid id,
+ RemoteEventListener listener,
+ long minWarning,
+ MarshalledObject handback)
+ throws RemoteException, ThrowThis
+ {
+ ready.check();
+ final LeaseSet set = getSet(id);
+
+ if (listener == null) {
+ minWarning = NO_LISTENER;
+ handback = null;
+ } else if (minWarning < 0) {
+ throw new IllegalArgumentException(
+ "LeaseRenewalSet.setExpirationWarningListener:minWarning " +
+ "must be positive");
+ } else {
+ listener = (RemoteEventListener) listenerPreparer.prepareProxy(
+ listener);
+ }
+
+ try {
+ store.acquireMutatorLock();
+ synchronized (set) {
+ ensureCurrent(set);
+
+ try {
+ final boolean haveBefore = set.haveWarningRegistration();
+ final EventRegistration rslt =
+ set.setExpirationWarningListener(
+ listener, minWarning, handback);
+ final boolean haveAfter = set.haveWarningRegistration();
+
+ if (haveAfter || (haveBefore != haveAfter)) {
+ // Either we had a registration before and we
+ // don't now, we do now and not before, or we
+ // had one before and we still do. In the
+ // first two cases we have to wack the
+ // expiration manager so it can schedule the
+ // right task. In the last case wack the
+ // expiration manager so it can reschedule in case
+ // minWarning has changed.
+ expMgr.reschedule(set);
+ }
+
+ return rslt;
+ } catch (IOException e) {
+ // This means the listener could not be serialized,
+ // re-throw as an IllegalArgumentException
+ throw new IllegalArgumentException("Passed a listener " +
+ "that could not be serialized");
+ }
+ }
+ } finally {
+ store.releaseMutatorLock();
+ }
+ }
+
+ /**
+ * Remote a set if its expiration time has been reached.
+ */
+ void expireIfTime(LeaseSet set) {
+ try {
+ store.acquireMutatorLock();
+
+ synchronized (set) {
+ if (isCurrent(set)) {
+ // Someone must have renewed the lease...don't expire
+ return;
+ }
+
+ removeSet(set);
+ }
+ } finally {
+ store.releaseMutatorLock();
+ }
+ }
+
+ /**
+ * Schedule the sending of an expiration warning event.
+ * This could be a method on the set itself but this keeps all
+ * of the high level synchronization logic in one file.
+ */
+ void sendWarningEvent(LeaseSet set) {
+ // We don't need to acquire the store lock because we
+ // won't be mutating any persistent state.
+ // We will be reading state of the set so we do need to
+ // sync on it to ensure we get a constant view
+ synchronized (set) {
+ if (!isCurrent(set)) {
+ // Must have just been canceled or expired, don't send event
+ return;
+ }
+
+ set.sendWarningEvent();
+ }
+ }
+
+ // Inherit java doc from super type
+ public EventRegistration setRenewalFailureListener(
+ Uuid id,
+ RemoteEventListener listener,
+ MarshalledObject handback)
+ throws RemoteException, ThrowThis
+ {
+ ready.check();
+ final LeaseSet set = getSet(id);
+
+ if (listener == null) {
+ handback = null;
+ } else {
+ listener = (RemoteEventListener) listenerPreparer.prepareProxy(
+ listener);
+ }
+
+ try {
+ store.acquireMutatorLock();
+ synchronized (set) {
+ ensureCurrent(set);
+
+ try {
+ return set.setRenewalFailureListener(
+ listener, handback);
+ } catch (IOException e) {
+ // This means the listener could not be serialized,
+ // re-throw as an IllegalArgumentException
+ throw new IllegalArgumentException("Passed a listener " +
+ "that could not be serialized");
+ }
+ }
+ } finally {
+ store.releaseMutatorLock();
+ }
+ }
+
+ /**
+ * Handle failures to renew a lease by removing the lease from its set
+ * and if needed schedule sending an event.
+ * @param clw the wrapped client lease for the lease that could not
+ * be renewed. <code>clw.isDeformed</code> must be
+ * <code>false</code>.
+ */
+ void renewalFailure(ClientLeaseWrapper clw) {
+ final LeaseSet set = clw.getLeaseSet();
+ if (set == null) {
+ // set must have just been removed, no state to update, no
+ // events to send, just return
+ return;
+ }
+
+ try {
+ store.acquireMutatorLock();
+
+ synchronized (set) {
+ logger.log(Level.INFO, "Lease renewal failed for {0}", clw);
+
+ if (!isCurrent(set)) {
+ // expired, no state to update, no
+ // events to send, just return
+ return;
+ }
+
+ set.renewalFailure(clw);
+
+ // Remove from LRM. Should already be
+ // gone from LRM, but it might have been added back in
+ // between the time the renewal failure occurred and
+ // when we got around to processing it (that is now).
+ // Making sure it is gone will keep things
+ // consistent.
+ try {
+ lrm.remove(clw);
+ } catch (UnknownLeaseException e) {
+ // As long as the lease is gone we don't care
+ }
+ }
+ } finally {
+ store.releaseMutatorLock();
+ }
+ }
+
+
+ /**
+ * Remove a lease that has reached its desired expiration.
+ * @param clw the wrapped client lease for the lease that we are done with
+ */
+ void desiredExpirationReached(ClientLeaseWrapper clw) {
+ final LeaseSet set = clw.getLeaseSet();
+ if (set == null) {
+ // set must have just been removed, no state to update, no
+ // events to send, just return
+ return;
+ }
+
+ try {
+ store.acquireMutatorLock();
+
+ synchronized (set) {
+ if (!isCurrent(set)) {
+ // expired, no state to update, no events to send,
+ // just return
+ return;
+ }
+
+ // Make sure the lease is still in the set it thinks
+ // it is
+ if (!set.doesContainWrapper(clw)) {
+ // Must have been removed somewhere else
+ // forget about this event
+ return;
+ }
+
+ // The client could have re-added the lease to the set
+ // after the event occured but before we processed the
+ // event. Check to make sure it really should be
+ // removed.
+ final long desiredExpiration = clw.getMembershipExpiration();
+ if (desiredExpiration > System.currentTimeMillis()) {
+ // Not dead yet...still in the rest of our tables,
+ // just need to make sure it is in the LRM
[... 1250 lines stripped ...]