/*
* 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.mercury;
import com.sun.jini.landlord.LeasedResource;
import net.jini.id.Uuid;
import net.jini.security.ProxyPreparer;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.rmi.MarshalledObject;
import java.rmi.RemoteException;
import java.util.Date;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Map;
import net.jini.core.event.RemoteEventListener;
/**
* The <tt>ServiceRegistration</tt> class serves as the server-side abstraction
* that maintains client registration state information. It implements the
* <tt>LeasedResource</tt> interface to allow it to be used with the
* <tt>Landlord</tt> framework. It implements the <tt>Comparable</tt>
* interface so that it can be used in <tt>SortedMap</tt> collections.
*
* @author Sun Microsystems, Inc.
*
* @since 1.1
*/
class ServiceRegistration implements LeasedResource, Comparable, Serializable {
private static final long serialVersionUID = 2L;
/** Unique identifier object */
private final Uuid cookie;
/** The current expiration for this registration */
private long expiration = 0;
/** The prepared, client-provided notification target. */
// This field is transient in order to allow readObject/writeObject
// to un/wrap the the listener to/from a MarshalledObject.
// This is done to prevent the corruption of the input stream.
// For example, if the associated codebase of the listener object
// is no longer valid, then the stream is corrupted since the object
// can't be reconstructed. Wrapping the reference in a
// MarshalledObject always allows us to read from the stream.
// We might not be able to reconstruct the listener field, but
// the rest of the objects in the stream will be fine.
private transient RemoteEventListener preparedEventTarget = null;
/** The marshalled form of the client-provided notification target. */
private MarshalledObject marshalledEventTarget = null;
/** Event log iterator. */
// This field is transient because event state info is persisted
// separately from the registration state info. It is (re)constructed
// upon service initialization.
private transient EventLogIterator eventIterator = null;
/**
* Map of collected <tt>EventID</tt>'s that resulted in an
* UnknownEventException for the current <code>
* eventTarget</code>. This <code>Map</code> is checked
* upon each mailbox notification for this registration.
* If the received event has the same <tt>EventID</tt>
* as the one contained in this <code>Map</code> then an
* <code>UnknownEventException</code>
* is propagated back to the sender and the event is not logged.
* This structure is also consulted before each event delivery request.
* If the event to be delivered has an <tt>EventID</tt> that is
* contained in this <tt>Map</tt>, then event delivery is cancelled
* for that particular event. Note that this structure gets cleared
* whenever a target listener is (re)set on the assumption that an
* active (re)set will provide a new/better target listener that might
* be able to handle these events.
*/
private Map unknownEvents = new HashMap();
/**
* Unique identifier object for the currently enabled
* (client-side) remote event iterator.
*/
private Uuid remoteEventIteratorID;
/**
* Lock object used to coordinate event delivery via the iterator.
* Has to be a serializable object versus just a plain Object.
*/
private final String iteratorNotifier = new String();
/** Convenience constructor */
public ServiceRegistration(Uuid cookie, EventLogIterator eventIterator) {
this.cookie = cookie;
this.eventIterator = eventIterator;
}
// inherit javadoc from parent
public void setExpiration(long newExpiration) {
expiration = newExpiration;
}
// inherit javadoc from parent
public long getExpiration() {
return expiration;
}
// inherit javadoc from parent
public Uuid getCookie() {
return cookie;
}
/**
* Return the identity map of EventIDs that caused an
* UnknownEventException to be generated for the current
* notification target. A Map is used instead of a generic
* Collection since the space/time tradeoff for searches
* seems worth it.
*/
public Map getUnknownEvents() {
return unknownEvents;
}
/**
* Get the reference to the prepared,
* client-supplied notification target
*/
public RemoteEventListener getEventTarget() {
return preparedEventTarget;
}
/** Set the reference to the client-supplied notification target */
public void setEventTarget(RemoteEventListener preparedTarget)
throws IOException
{
if (preparedTarget == null) {
preparedEventTarget = null;
marshalledEventTarget = null;
} else {
preparedEventTarget = preparedTarget;
marshalledEventTarget = new MarshalledObject(preparedTarget);
}
}
/** Get the remote iterator id */
public Uuid getRemoteEventIteratorID() {
return remoteEventIteratorID;
}
/**
* Get the remote iterator notifier object. This is used to coordinate
* notifications between event delivery and event reception via the
* iterator.
*/
public Object getIteratorNotifier() {
return iteratorNotifier;
}
/** Set the remote iterator id */
public void setRemoteEventIteratorID(Uuid id) {
remoteEventIteratorID = id;
}
/**
* Returns <code>true</code> if an event target is currently set and
* false otherwise.
*/
public boolean hasEventTarget() {
return (marshalledEventTarget != null);
}
/*
* Method that restores internal, transient state.
* Note:
* This method attempts to prepare the target listener object, which
* might involve a remote invocation. Therefore, calling this method
* while holding a lock should be avoided.
*/
public void restoreTransientState(ProxyPreparer targetPreparer)
throws IOException, ClassNotFoundException
{
if (targetPreparer == null) {
throw new NullPointerException(
"targetPreparer cannot be null");
}
if (marshalledEventTarget != null) {
RemoteEventListener unprepared =
(RemoteEventListener)marshalledEventTarget.get();
preparedEventTarget = (RemoteEventListener)
targetPreparer.prepareProxy(unprepared);
}
/*
* Note: Would like to defer preparation until listener
* is needed (i.e. in getEventTarget()). Unfortunately,
* listeners are obtained within "locks" and proxy
* preparation (i.e. possible remote calls) should
* not be done while holding those locks.
*/
}
/**
* Get the reference to the registration's associated
* <tt>EventLogIterator</tt>
*/
public EventLogIterator iterator() {
return eventIterator;
}
/**
* Set the reference for this registration's
* <tt>EventLogIterator</tt>
*/
public void setIterator(EventLogIterator iter) {
eventIterator = iter;
}
/**
* Primary sort by leaseExpiration, secondary by leaseID. The
* secondary sort is immaterial, except to ensure a total order
* (required by TreeMap).
*/
public int compareTo(Object obj) {
ServiceRegistration reg = (ServiceRegistration)obj;
if (this == reg)
return 0;
if ( expiration < reg.expiration ||
(expiration == reg.expiration &&
( (cookie.getMostSignificantBits() <
reg.cookie.getMostSignificantBits())
&& (cookie.getLeastSignificantBits() <
reg.cookie.getLeastSignificantBits()) ) ))
return -1;
return 1;
}
// inherit documentation from supertype
public String toString () {
return getClass().getName() + ":" + cookie.toString();
}
//TODO - implement readObject() for structural checks
/**
* Utility method to display debugging information to the
* provided <tt>Logger</tt>
*/
void dumpInfo(Logger logger) {
logger.log(Level.FINEST, "{0}", this.toString());
logger.log(Level.FINEST, "Expires at: {0}", new Date(expiration));
logger.log(Level.FINEST, "Prepared target is: {0}", preparedEventTarget);
logger.log(Level.FINEST, "Marshalled target is: {0}", marshalledEventTarget);
logger.log(Level.FINEST, "Unknowns: {0}",
new Integer(unknownEvents.size()));
// eventIterator is null upon recovery phase which
// is before the transient state gets rebuilt.
if (eventIterator != null) {
try {
logger.log(Level.FINEST, "hasNext: {0}",
Boolean.valueOf(eventIterator.hasNext()));
} catch (IOException ioe) {
logger.log(Level.FINEST, "hasNext exception.", ioe);
}
}
}
}