/* ************************************************************************* *
* *
* Copyright (c) 2004 Peter Cappello <cappello@cs.ucsb.edu> *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
* *
* ************************************************************************* */
/**
* A Remote proxy: A Service interacts with a Remote Service via its Proxy.
*
* @author Peter Cappello
*/
/*
* !! One thread suffices to service ALL Proxy objects with respect to Lease
* expiration/renewal.
*
* The Proxy also is responsible for maintaining state information pertaining to
* the Service for which it is a proxy. Even Remote method invocations on its
* containing Service from the Service which it serves as Proxy may need to
* invoke proxy methods to maintain state. For example, a Host may send a
* TaskServer a ProcessResult command. The Host's proxy must update its state to
* reflect that the actual Host has completed some particular Task and update
* Task execution-time statistics for the actual Host.
*
* A Proxy also is a Thread-safe lease manager (evict, offerRenewal, renew).
*/
package jicosfoundation;
import java.rmi.*;
public abstract class Proxy extends Thread
{
// constants
private static final CommandSynchronous PING = new Ping();
// attributes
private ServiceName serviceName; // !! eliminate
private Service service; // !! remove either service or remoteService
private ServiceImpl containingServiceImpl;
protected boolean kill;
private RemoteExceptionHandler remoteExceptionHandler;
private Mailer mailer;
private boolean anInternalService;
private boolean isUnreachable = false;
//private Proxy forwardingProxy;
// convenience
private Service remoteService;
// !! part of LeaseManager
private long term;
private long expirationTime;
private long remainingTime;
// !! When all components are Proxified, remove Mail object from Service
// Put in Proxies ??
public Proxy( ServiceName serviceName,
ServiceImpl containingServiceImpl,
RemoteExceptionHandler remoteExceptionHandler,
long term
)
{
super( "Jicos Proxy for " + serviceName );
assert serviceName != null;
this.serviceName = serviceName;
service = serviceName.service();
constructProxy(service, containingServiceImpl, remoteExceptionHandler, term);
}
public Proxy( Service service, ServiceImpl containingServiceImpl,
RemoteExceptionHandler remoteExceptionHandler, long term
)
{
assert service != null;
constructProxy(service, containingServiceImpl, remoteExceptionHandler, term);
}
void constructProxy( Service service, ServiceImpl containingServiceImpl,
RemoteExceptionHandler remoteExceptionHandler, long term
)
{
assert service != null;
this.service = service;
this.containingServiceImpl = containingServiceImpl;
this.remoteExceptionHandler = remoteExceptionHandler;
this.term = term;
// !! after proxified, use Mail constructor.
remoteService = service;
if ( containingServiceImpl != null )
{
// Proxy for a Remote Service
mailer = containingServiceImpl.addMail( this, remoteExceptionHandler );
}
else
{
// Proxy for an internal Service
anInternalService = true;
}
renew();
start();
}
public void containingServiceImpl( ServiceImpl containingServiceImpl )
{
assert containingServiceImpl != null;
this.containingServiceImpl = containingServiceImpl;
}
abstract public void evict();
public Object execute( CommandSynchronous command,
RemoteExceptionHandler remoteExceptionHandler )
{
assert remoteExceptionHandler != null;
return command.execute( this, remoteExceptionHandler );
}
public void execute( Command command ) { command.execute( this ); }
public Service getService() { return service; }
public boolean isUnreachable() { return isUnreachable; }
public synchronized void kill() { kill = true; }
public boolean offerRenewal()
{
if ( containingServiceImpl == null )
{
// internal Host: Ignore
renew();
return true;
}
try
{
remoteService.executeCommand(containingServiceImpl, PING);
}
catch ( RemoteException e )
{
System.out.println("Proxy.offerRenewal: PING RemoteException.");
e.printStackTrace();
LogManager.getLogger(this).log( LogManager.INFO,
"RemoteException (" + e.getCause().getClass().getName() + ")");
return false; // remoteService is not responding.
}
// catch ( Exception e )
// {
// System.out.println("Proxy.offerRenewal: PING Exception.");
// e.printStackTrace();
// System.exit( 1 );
// }
renew();
return true;
}
public void remoteExceptionHandler( RemoteExceptionHandler remoteExceptionHandler )
{
assert remoteExceptionHandler != null;
this.remoteExceptionHandler = remoteExceptionHandler;
}
public Service remoteService() { return remoteService; }
public synchronized void renew()
{
expirationTime = System.currentTimeMillis() + term;
remainingTime = term;
}
public void run()
{
while ( true )
{
try
{
sleep ( remainingTime );
}
catch ( InterruptedException ignore ) {}
if ( kill )
{
System.out.println("Proxy.run: exiting ... (killing thread)");
return; // kill this thread
}
synchronized ( this )
{
remainingTime = expirationTime - System.currentTimeMillis();
if ( remainingTime < 0 && ! offerRenewal() )
{
System.out.println("Proxy.run: about to invoke evict. remainingTime: " + remainingTime );
evict();
return; // kill thread
}
}
}
}
public void sendCommand( Command command )
{
assert command != null;
if ( anInternalService )
{
try
{
command.execute( (ServiceImpl) remoteService );
}
catch ( Exception exception )
{
containingServiceImpl.exceptionHandler( exception );
}
return;
}
mailer.add( command );
}
public ServiceName serviceName() { return serviceName; }
public void setTerm( long term )
{
assert term > 0;
this.term = term;
}
public void unreachable() { isUnreachable = true; }
}