/*
* 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.fiddler;
import com.sun.jini.fiddler.Fiddler;
import com.sun.jini.fiddler.FiddlerLeaseMap;
import com.sun.jini.fiddler.ProxyUtil;
import com.sun.jini.proxy.ConstrainableProxyUtil;
import com.sun.jini.lease.AbstractLease;
import net.jini.id.ReferentUuid;
import net.jini.id.ReferentUuids;
import net.jini.id.Uuid;
import net.jini.security.proxytrust.ProxyTrustIterator;
import net.jini.security.proxytrust.SingletonProxyTrustIterator;
import net.jini.core.constraint.MethodConstraints;
import net.jini.core.constraint.RemoteMethodControl;
import net.jini.core.constraint.InvocationConstraints;
import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseMap;
import net.jini.core.lease.UnknownLeaseException;
import javax.security.auth.Subject;
import java.lang.reflect.Method;
import java.io.InvalidObjectException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.rmi.RemoteException;
/**
* When the Fiddler implementation of the lookup discovery service grants
* a lease on a registration requested by a client, a proxy to that lease
* is provided to allow the client to interact with the granted lease.
* This class is the implementation of that proxy.
* <p>
* Clients only see instances of this class via the <code>Lease</code>
* interface.
*
* @author Sun Microsystems, Inc.
*
*/
class FiddlerLease extends AbstractLease
implements ReferentUuid
{
private static final long serialVersionUID = 2L;
/**
* The reference to the back-end server of the lookup discovery service
* that granted this lease (the granting entity).
*
* @serial
*/
final Fiddler server;
/**
* The unique ID associated with the server referenced in this class
* (used to compare server references).
*
* @serial
*/
final Uuid serverID;
/**
* The unique identifier assigned by the lookup discovery service to
* the registration to which this lease corresponds.
*
* @serial
*/
final Uuid registrationID;
/**
* The internal identifier assigned to this lease by the granting entity.
*
* @serial
*/
final Uuid leaseID;
/**
* Public static factory method that creates and returns an instance of
* <code>FiddlerLease</code>. If the server associated with this proxy
* implements <code>RemoteMethodControl</code>, then the object returned by
* this method will also implement <code>RemoteMethodControl</code>.
*
* @param server reference to the server object through which
* communication occurs between the client-side and
* server-side of the associated service.
* @param serverID the service ID of the service referenced by the
* server parameter
* @param registrationID the unique identifier assigned by the service to
* each instance of this proxy
* @param leaseID identifier assigned to this lease by the
* granting entity
* @param expiration the time of expiration of the lease being granted
*
* @return an instance of <code>FiddlerLease</code> that implements
* <code>RemoteMethodControl</code> if the given
* <code>server</code> does.
*/
public static FiddlerLease createLease(Fiddler server,
Uuid serverID,
Uuid registrationID,
Uuid leaseID,
long expiration)
{
if(server instanceof RemoteMethodControl) {
return new ConstrainableFiddlerLease(server,
serverID,
registrationID,
leaseID,
expiration,
null);//method constraints
} else {
return new FiddlerLease(server,
serverID,
registrationID,
leaseID,
expiration);
}//endif
}//end createLease
/**
* Constructs a proxy to the lease the Fiddler implementation of the
* lookup discovery service places on a client's requested registration.
* The lease corresponding to the constructed proxy will have as its
* expiration time, the time input to the <code>expiration</code>
* parameter.
*
* @param server reference to the back-end server of the lookup
* discovery service that granted this lease (the
* granting entity)
* @param serverID the service ID of the service referenced by the
* server parameter
* @param registrationID unique identifier assigned to the registration
* to which this lease corresponds
* @param leaseID identifier assigned to this lease by the
* granting entity
* @param expiration the time of expiration of the lease being granted
*/
private FiddlerLease(Fiddler server,
Uuid serverID,
Uuid registrationID,
Uuid leaseID,
long expiration)
{
super(expiration);
this.server = server;
this.serverID = serverID;
this.registrationID = registrationID;
this.leaseID = leaseID;
}//end constructor
/**
* Creates a <code>LeaseMap</code> object that can contain leases whose
* renewal or cancellation can be batched. Additionally, upon creating
* the map the current lease is placed in the map as a key, and
* the value of the <code>duration</code> parameter is placed in
* the map as the lease's corresponding mapped value of type
* <code>Long</code>.
*
* @param duration the amount of time (in milliseconds) during which
* the lease being placed in the map will remain in
* effect. This value will be converted to an instance
* of <code>Long</code> prior to being placed in the
* map with its associated lease.
* the lease in the map.
*
* @return an instance of <code>LeaseMap</code> that contains as its
* first mapping, the ordered pair consisting of this lease
* and the value of the <code>duration</code> parameter.
*
* @see net.jini.core.lease.Lease#createLeaseMap
*/
public LeaseMap createLeaseMap(long duration) {
return FiddlerLeaseMap.createLeaseMap(this, duration);
}
/**
* Examines the input parameter to determine if that parameter, along
* with the current lease (the current instance of this class), can
* be batched in a <code>LeaseMap</code>.
* <p>
* For this implementation of the service, two leases can be batched
* (placed in the same service-specific instance of <code>LeaseMap</code>)
* if those leases satisfy the following conditions:
* <p><ul>
* <li> the leases are the same type; that is, the leases are both
* instances of the same, service-specific <code>Lease</code>
* implementation
* <li> the leases were granted by the same backend server
*
* @param lease reference to the <code>Lease</code> object that this
* method examines for batching compatibility with the
* the current current instance of this class
*
* @return <code>true</code> if the input parameter is compatible for
* batching with the current current instance of this class,
* <code>false</code> otherwise
*
* @see net.jini.core.lease.Lease#canBatch
*/
public boolean canBatch(Lease lease) {
return ( (lease instanceof FiddlerLease)
&& (serverID.equals(((FiddlerLease)lease).serverID)) );
}//end FiddlerLease.canBatch
/**
* Returns a reference to the back-end server of the lookup discovery
* service that granted this lease.
*
* @return an instance of the Fiddler implementation of the lookup
* discovery service.
*/
Fiddler getServer() {
return server;
}
/**
* Returns the unique ID associated with the server referenced in this
* class.
*
* @return an instance of the <code>Uuid</code> that contains the
* universally unique ID associated with the server referenced
* in this class.
*/
Uuid getServerID() {
return serverID;
}
/**
* Returns the unique identifier assigned by the lookup discovery
* service to the registration to which the current lease corresponds.
*
* @return a <code>net.jini.id.Uuid</code> value
* corresponding to the unique ID assigned to the registration
* associated with the current lease.
*/
Uuid getRegistrationID() {
return registrationID;
}
/**
* Returns the identifier assigned to the current lease by the entity
* that granted it.
*
* @return a <code>long</code> value corresponding to the ID assigned
* to the current lease.
*/
Uuid getLeaseID() {
return leaseID;
}
/**
* Replaces the current value of the <code>expiration</code> field
* (defined in the <code>AbstractLease</code> super class) with the
* value contained in the input parameter. The value contained in the
* <code>expiration</code> field represents the absolute (non-relative)
* time (in milliseconds) at which the current lease will expire.
*
* @param expiration the new value of the <code>expiration</code> field
*/
void setExpiration(long expiration) {
this.expiration = expiration;
}
/**
* This method allows the entity to which the current lease is granted
* (the lease holder) to indicate that it is no longer interested
* in the resources provided to the entity by the lookup discovery
* service. When an entity invokes this method, the overall effect
* is the same as if the lease expired, except that expiration occurs
* immediately instead of at the end of a pre-agreed duration.
*
* @throws net.jini.core.lease.UnknownLeaseException this exception occurs
* when the lease being cancelled is unknown to the lease grantor.
*
* @throws java.rmi.RemoteException typically, this exception occurs when
* there is a communication failure between the client and the
* server. When this exception does occur, the lease may or may
* not have been cancelled successfully.
*
* @see net.jini.core.lease.Lease#cancel
*/
public void cancel() throws UnknownLeaseException, RemoteException {
server.cancelLease(registrationID, leaseID);
}
/**
* This method allows the entity to which the current lease is granted
* (the lease holder) to indicate that it is still interested in the
* resources of the lookup discovery service, and to request continued
* access to those resources for an amount of time (in milliseconds)
* relative to the current time. That is, the duration is not added
* added to the current expiration, but is added to the current time.
* <p>
* Note that the duration of the renewed lease will be no greater than
* the requested duration, and may be less than that duration.
*
* @param duration the requested duration for the lease being renewed
*
* @return <code>long</code> value representing the actual duration that
* was granted for the renewed lease. Note that the actual
* duration granted and returned by this method may be less than
* the duration requested.
*
* @throws net.jini.core.lease.UnknownLeaseException this exception occurs
* when the lease being renewed does not exist, or is unknown
* to the lease grantor; typically because the lease has expired.
*
* @throws java.rmi.RemoteException typically, this exception occurs when
* there is a communication failure between the client and the
* server. When this exception does occur, the lease may or may
* not have been renewed successfully.
*
* @see net.jini.core.lease.Lease#renew
* @see com.sun.jini.lease.AbstractLease#renew
* @see com.sun.jini.lease.AbstractLease#doRenew
*/
protected long doRenew(long duration)
throws UnknownLeaseException, RemoteException
{
return server.renewLease(registrationID, leaseID, duration);
}
/**
* Returns the universally unique identifier that has been assigned to the
* resource this proxy represents.
*
* @return the instance of <code>Uuid</code> that is associated with the
* resource this proxy represents. This method will not return
* <code>null</code>.
*
* @see net.jini.id.ReferentUuid
*/
public Uuid getReferentUuid() {
return leaseID;
}
/**
* For any instance of this class, returns the hashcode value generated
* by the hashCode method of the lease ID associated with the current
* instance of this lease.
*
* @return <code>int</code> value representing the hashcode for an
* instance of this class.
*/
public int hashCode() {
return leaseID.hashCode();
}
/**
* For any instance of this class, indicates whether the object input
* to this method is equal to the current instance of this class; where
* equality of leases granted by a lookup discovery service is defined by
* reference equality. That is, two leases are equal if they reference
* (are proxies to) the same backend lease server.
*
* @param obj reference to the object that is to be compared to the
* object on which this method is invoked.
*
* @return <code>true</code> if the object input is referentially
* equal to the object on which this method is invoked;
* <code>false</code> otherwise.
*/
public boolean equals(Object obj) {
return ReferentUuids.compare(this,obj);
}
/** When an instance of this class is deserialized, this method is
* automatically invoked. This implementation of this method validates
* the state of the deserialized instance.
*
* @throws <code>InvalidObjectException</code> if the state of the
* deserialized instance of this class is found to be invalid.
*/
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject();
/* Verify server */
if(server == null) {
throw new InvalidObjectException("FiddlerLease.readObject "
+"failure - server "
+"field is null");
}//endif
/* Verify serverID */
if(serverID == null) {
throw new InvalidObjectException("FiddlerLease.readObject "
+"failure - serverID "
+"field is null");
}//endif
/* Verify registrationID */
if(registrationID == null) {
throw new InvalidObjectException("FiddlerLease.readObject "
+"failure - registrationID "
+"field is null");
}//endif
}//end readObject
/** During deserialization of an instance of this class, if it is found
* that the stream contains no data, this method is automatically
* invoked. Because it is expected that the stream should always
* contain data, this implementation of this method simply declares
* that something must be wrong.
*
* @throws <code>InvalidObjectException</code> to indicate that there
* was no data in the stream during deserialization of an
* instance of this class; declaring that something is wrong.
*/
private void readObjectNoData() throws InvalidObjectException {
throw new InvalidObjectException("no data found when attempting to "
+"deserialize FiddlerLease instance");
}//end readObjectNoData
/** The constrainable version of the class <code>FiddlerLease</code>.
* <p>
* When a client obtains an instance of this proxy class, the client
* should not attempt to use the proxy until the client is assured
* that the proxy can be trusted. In addition to implementing the
* methods and mechanisms required by <code>RemoteMethodControl</code>,
* this class - in conjunction with the service's
* <code>ProxyVerifier</code> class, helps provide a mechanism
* for verifying trust in the proxy on behalf of a client.
* <p>
* In order to verify that an instance of this class is trusted,
* trust must be verified in all subsidiary objects (contained in that
* instance) through which the client ultimately makes calls (local or
* remote). With respect to this class, the <code>server</code> field
* is a proxy object through which the client makes remote calls to the
* service's backend. Therefore, trust in that object must be
* verified. Additionally, this class also contains a field of type
* <code>Uuid</code> (<code>registrationID</code> which should be
* tested for trust. Consider the following diagram:
* <p>
* <pre>
* FiddlerLease {
* Fiddler server
* Uuid registrationID
* }//end FiddlerLease
* </pre>
* <p>
* Thus, in order to verify that an instance of this class is trusted,
* trust must be verified in the following objects from the diagram
* above:
* <ul><li> server
* <li> registrationID
* </ul>
*
* When a client obtains an instance of this proxy class, the
* deserialization process which delivers the proxy to the client
* invokes the <code>readObject</code> method of this class. Part of
* trust verification is performed in the <code>readObject</code> method,
* and part is performed when the client prepares the proxy. Thus, this
* class' participation in the trust verification process can be
* summarized as follows:
* <p>
* <ul>
* <li> server
* <ul>
* <li> readObject
* <ul>
* <li> verify server != null
* <li> verify server implements RemoteMethodControl
* <li> verify server's method constraints are the same
* as those placed on the corresponding public Remote
* methods of its outer proxy class
* </ul>
* <li> proxy preparation
* <ul>
* <li> Security.verifyObjectTrust() which calls
* <li> ProxyVerifier.isTrustedObject() which calls
* <li> canonicalServerObject.checkTrustEquivalence(server)
* (whose implementation is supplied by the particular
* RMI implementation that was used to export the server)
* </ul>
* </ul>
* <li> registrationID
* <ul><li> readObject
* <ul><li> verify registrationID != null</ul>
* </ul>
* </ul>
*
* @since 2.0
*/
static final class ConstrainableFiddlerLease extends FiddlerLease
implements RemoteMethodControl
{
static final long serialVersionUID = 2L;
/* Convenience fields containing, respectively, the renew and cancel
* methods defined in the Lease interface. These fields are used in
* the method mapping array, and when retrieving method constraints
* for comparison in canBatch().
*/
private static final Method renewMethod =
ProxyUtil.getMethod(Lease.class,
"renew",
new Class[] {long.class} );
private static final Method cancelMethod =
ProxyUtil.getMethod(Lease.class,
"cancel",
new Class[] {} );
/** Array containing element pairs in which each pair of elements
* represents a correspondence 'mapping' between two methods having
* the following characteristics:
* - the first element in the pair is one of the public, remote
* method(s) that may be invoked by the client through the proxy
* class that this class extends
* - the second element in the pair is the method, implemented
* in the backend server class, that is ultimately executed in
* the server's backend when the client invokes the corresponding
* method in this proxy
*/
private static final Method[] methodMapArray =
{
renewMethod, ProxyUtil.getMethod(Fiddler.class,
"renewLease",
new Class[] {Uuid.class,
Uuid.class,
long.class} ),
cancelMethod, ProxyUtil.getMethod(Fiddler.class,
"cancelLease",
new Class[] {Uuid.class,
Uuid.class} )
};//end methodMapArray
/** In order to determine if this lease can be batched with another
* given lease, the method <code>canBatch</code> must verify that
* the corresponding methods of each lease have equivalent
* constraints. The array defined here contains the set of methods
* whose constraints will be compared in <code>canBatch</code>.
*/
private static final Method[] canBatchMethodMapArray =
{ renewMethod, renewMethod,
cancelMethod, cancelMethod
};//end canBatchMethodMapArray
/** Client constraints placed on this proxy (may be <code>null</code>).
*
* @serial
*/
private MethodConstraints methodConstraints;
/** Constructs a new <code>ConstrainableFiddlerLease</code> instance.
* <p>
* For a description of all but the <code>methodConstraints</code>
* argument (provided below), refer to the description for the
* constructor of this class' super class.
*
* @param methodConstraints the client method constraints to place on
* this proxy (may be <code>null</code>).
*/
private ConstrainableFiddlerLease(Fiddler server,
Uuid serverID,
Uuid registrationID,
Uuid leaseID,
long expiration,
MethodConstraints methodConstraints)
{
super( constrainServer(server, methodConstraints),
serverID, registrationID, leaseID, expiration );
this.methodConstraints = methodConstraints;
}//end constructor
/**
* Examines the input parameter to determine if that parameter, along
* with the current lease (the current instance of this class), can
* be batched in a <code>LeaseMap</code>.
* <p>
* For this implementation of the service, two leases can be
* batched (placed in the same service-specific instance of
* <code>LeaseMap</code>) if those leases satisfy the following
* conditions:
* <p><ul>
* <li> the leases are the same type; that is, the leases are
* both instances of the same, constrainable, service-specific
* <code>Lease</code> implementation
* <li> the leases were granted by the same backend server
* <li> the leases have the same constraints
*
* @param lease reference to the <code>Lease</code> object that this
* method examines for batching compatibility with the
* the current instance of this class
*
* @return <code>true</code> if the input parameter is compatible for
* batching with the current instance of this class,
* <code>false</code> otherwise
*
* @see net.jini.core.lease.Lease#canBatch
*/
public boolean canBatch(Lease lease) {
if( !(super.canBatch(lease)) ) return false;
/* Non-constrainable batch criteria satisfied, now handle
* constrainable case.
*/
if( !(lease instanceof ConstrainableFiddlerLease) ) return false;
/* Compare constraints */
return ConstrainableProxyUtil.equivalentConstraints
( methodConstraints,
((ConstrainableFiddlerLease)lease).methodConstraints,
canBatchMethodMapArray );
}//end ConstrainableFiddlerLease.canBatch
/** Returns a copy of the given server proxy having the client method
* constraints that result after the specified method mapping is
* applied to the given client method constraints.
*/
private static Fiddler constrainServer( Fiddler server,
MethodConstraints constraints )
{
MethodConstraints newConstraints
= ConstrainableProxyUtil.translateConstraints(constraints,
methodMapArray);
RemoteMethodControl constrainedServer =
((RemoteMethodControl)server).setConstraints(newConstraints);
return ((Fiddler)constrainedServer);
}//end constrainServer
/** Returns a new copy of this proxy class
* (<code>ConstrainableFiddlerLease</code>) with its client
* constraints set to the specified constraints. A <code>null</code>
* value is interpreted as mapping all methods to empty constraints.
*/
public RemoteMethodControl setConstraints
(MethodConstraints constraints)
{
return (new ConstrainableFiddlerLease(server,
serverID,
registrationID,
leaseID,
expiration,
constraints));
}//end setConstraints
/** Returns the client constraints placed on the current instance
* of this proxy class (<code>ConstrainableFiddlerLease</code>).
* The value returned by this method can be <code>null</code>,
* which is interpreted as mapping all methods to empty constraints.
*/
public MethodConstraints getConstraints() {
return methodConstraints;
}//end getConstraints
/** Returns a proxy trust iterator that is used in
* <code>ProxyTrustVerifier</code> to retrieve this object's
* trust verifier.
*/
private ProxyTrustIterator getProxyTrustIterator() {
return new SingletonProxyTrustIterator(server);
}//end getProxyTrustIterator
/** Performs various functions related to the trust verification
* process for the current instance of this proxy class, as
* detailed in the description for this class.
*
* @throws <code>InvalidObjectException</code> if any of the
* requirements for trust verification (as detailed in the
* class description) are not satisfied.
*/
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
/* Note that basic validation of the fields of this class was
* already performed in the readObject() method of this class'
* super class.
*/
s.defaultReadObject();
/* Verify the server and its constraints */
ConstrainableProxyUtil.verifyConsistentConstraints
(methodConstraints,
server,
methodMapArray);
}//end readObject
}//end class ConstrainableFiddlerLease
}//end class FiddlerLease