/* ************************************************************************* *
* *
* 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. *
* *
* ************************************************************************* */
/**
* Generic distributed service.
* Extender of ServiceImpl: Your constructor must invoke setDepartments, passing
* it an array of Department objects, the 0th of which is assumed to be the asap
* "Department": A Command type is Associate with this "Department" if and only
* if the Command types is to be executed by the RMI thread executing the
* receiveCommands method.
* Such Command types must invoke neither wait() nor Remote methods.
*
* @version 1.0
* @author Peter Cappello
*/
package jicosfoundation;
import java.io.*;
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
abstract public class ServiceImpl extends UnicastRemoteObject
implements Service
{
// Constants
public static final Department ASAP_DEPARTMENT = null;
private static final int ASAP_DEPARTMENT_NUM = 0;
// Standard ServiceImpl parts
private ServiceImpl myService; // The service that extends me.
private ServiceName serviceName;
private Department[] departments;
private Class2Int command2Department;
private ProxyManager proxyManager = new ProxyManager( 100, 0.75f, 1 );
// private ProxyManager proxyManager = new ProxyManager();
// private BlockingQueue readyMailQ = new LinkedBlockingQueue();
private Set registry = Collections.synchronizedSet( new HashSet() );
/** Constructor.
* @throws RemoteException Since ServiceImpl implements Service, a Remote
* interface, its no-arg constructor
* throws RemoteException.
* @param command2DepartmentArray is an array of Class arrays. It has a
* Class[] for each Department. Each Class[] has a Class for each Command
* processed by the Department. Class[0] is assumed to be the set of Command
* objects that are processed asap (within the RMI-Thread: w/o
* Thread-switching). See the processMail method.
*/
protected ServiceImpl( Class[][] command2DepartmentArray )
throws RemoteException
{
serviceName = new ServiceName ( this );
command2Department = new Class2Int ( command2DepartmentArray );
}
/** Add a Mailer for this Service (Remote reference).
* @param address a Remote reference to the destination Service.
* @return a reference to the Mailer object just created.
* Client may cache this for subsequent use.
*/
protected synchronized final Mailer addMail( Proxy myProxy, //TODO evalute whether needs to be synchronized
RemoteExceptionHandler remoteExceptionHandler )
{
assert remoteExceptionHandler != null;
return new Mailer( this, remoteExceptionHandler, new LinkedBlockingQueue(), myProxy );
}
// !! eliminate
public final void addProxy( ServiceName serviceName, Proxy proxy )
{
assert serviceName != null;
assert proxy != null;
proxyManager.addProxy( serviceName, proxy );
}
public final void addProxy( Service service, Proxy proxy )
{
assert service != null;
assert proxy != null;
proxyManager.addProxy( service, proxy );
}
public void broadcast( Command command, Service fromService )
{
assert command != null;
for ( Iterator i = proxyManager.values().iterator(); i.hasNext(); )
{
Proxy proxy = (Proxy) i.next();
if ( fromService == null || ! fromService.equals( proxy.getService() ) )
{
proxy.execute( command );
}
}
}
public void broadcast( Command command, Service fromService, Collection proxies )
{
assert command != null;
assert proxies != null;
for ( Iterator i = proxies.iterator(); i.hasNext(); )
{
Proxy proxy = (Proxy) i.next();
if ( fromService == null || ! fromService.equals( proxy.getService() ) )
{
proxy.execute( command );
}
}
}
protected synchronized final void clean()
{
/* Remove all Mail objects from transient addresses that are inactive.
* What event prompts its invocation?
* "WSClock - A Simple and Effective Algorithm for Virtual
* Memory Management", Carr and Hennessey, Proc. 8th SOSP, Operating
* Systems, Review, 15(5), Dec. 1981.
*/
}
abstract protected void exceptionHandler( Exception exception );
/** Implementation of the Service execute Remote method.
* @param command The Command object to be executed remotely.
* @return the object that is returned by the Command's execute method.
* This depends on the actual Command type.
*/
@Override
public final Object executeCommand( Service sender, CommandSynchronous command )
{
return command.execute( myService );
}
public Proxy getProxy( Service service )
{
assert service != null;
return proxyManager.getProxy( service );
}
private void processCommands( Queue<Command> q) throws Exception
{
int department$ = 0;
for ( Iterator<Command> i = q.iterator(); i.hasNext(); )
{
Command command = i.next();
if ( command instanceof CommandList )
{
Queue<Command> queue = ((CommandList) command).q();
processCommands ( queue );
}
else
{
try
{
department$ = command2Department.map( command );
}
catch ( IllegalArgumentException e )
{
System.err.println("Unmapped Command: " + command.getClass() + " for " + myService.getClass() );
System.exit(1);
}
if ( department$ == ASAP_DEPARTMENT_NUM )
{
command.execute( this ); // Asap Command
}
else
{
departments[ department$ ].addCommand( command );
}
}
}
}
protected ProxyManager proxyManager() { return proxyManager; }
/** Implements the Service interface deliver method.
* It is internal to the foundation package. See the Service interface.
* @param marshalledCommandQ See the Service interface.
*/
@Override
public final void receiveCommands ( Service sender, Queue<Command> commandQ)
{
assert sender != null;
assert commandQ != null;
try
{
processCommands ( commandQ );
}
catch ( Exception exception )
{
System.out.println("ServiceImpl.receiveCommands: exception");
exception.printStackTrace();
System.exit( 1 );
}
}
protected void register ( Service service ) { registry.add ( service ); }
protected Proxy removeProxy( Service service )
{
assert service != null;
return proxyManager.removeProxy( service );
}
// !! currently assuming Proxy was put in proxyManager and was not removed.
public void sendCommand( Service service, Command command )
{
assert service != null;
Proxy proxy = proxyManager.getProxy( service );
proxy.execute( command );
}
public ServiceName serviceName() { return serviceName; }
/** Departments must be set by extensions using setDepartment. This
* typically is done in the extender's constructor, after invoking super.
* @param departments an array of Department objects. The 0th element is
* omitted, since this "Department" refers to the "ASAP_DEPARTMENT":
* Command objects associated with this "Department" are executed by the RMI
* thread that implements the deliver method.
*/
protected final void setDepartments( Department[] departments )
{
this.departments = departments;
}
/** A reference to an object that extends ServiceImpl, such as a Host
* object. This is needed so that Commands executed in the RMI deliver
* thread will have access to the Host's state, for example.
* @param myService reference to an object that extends ServiceImpl, such as
* a Host object.
*/
protected void setService ( ServiceImpl myService )
{
assert myService != null;
this.myService = myService;
}
public void shutdown(){ System.out.println("ServiceImpl.shutdown: wrong actual object");}
protected void unregister ( Service service ) { registry.remove ( service ); }
protected static byte[] objectToBytes( Object object ) throws java.io.IOException
{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream( byteArrayOutputStream );
objectOutputStream.writeObject( object );
objectOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
protected static Object bytesToObject( byte[] bytes )
throws java.io.IOException, ClassNotFoundException
{
if ( bytes == null )
{
return null;
}
ObjectInputStream objectInputStream = new ObjectInputStream( new ByteArrayInputStream( bytes ) );
return objectInputStream.readObject();
}
}