RemoteClientInvoker.java |
/*************************************** * * * JBoss: The OpenSource J2EE WebOS * * * * Distributable under LGPL license. * * See terms of license at gnu.org. * * * ***************************************/ package org.jboss.remoting; import org.jboss.remoting.marshal.MarshalFactory; import org.jboss.remoting.marshal.Marshaller; import org.jboss.remoting.marshal.UnMarshaller; import org.jboss.remoting.marshal.InvalidMarshallingResource; import org.jboss.remoting.transport.ClientInvoker; import org.jboss.remoting.loading.ClassByteClassLoader; import java.io.IOException; import java.util.Map; /** * RemoteClientInvoker is an abstract client part handler that implements the bulk of the heavy * lifting to process a remote method and dispatch it to a remote ServerInvoker and handle the result. <P> * <p/> * Specialized Client/Server Invokers might add additional functionality as part of the invocation - such as * delivering queued notifcations from a remote server by adding the notification objects during each invocation * to the invocation result payload and then having the client re-dispatch the notifications locally upon * receiving the return invocation result. * * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a> * @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a> * @version $Revision: 1.2.8.2 $ */ public abstract class RemoteClientInvoker extends AbstractInvoker implements ClientInvoker { protected boolean connected = false; private Marshaller marshaller; private UnMarshaller unmarshaller; private String dataType; public RemoteClientInvoker(InvokerLocator locator) { super(locator); } /** * transport a request against a remote ServerInvoker * * @param invocationReq * @return * @throws Throwable */ public Object invoke(InvocationRequest invocationReq) throws Throwable { Object returnValue = null; int invokeCount = 0; if (log.isDebugEnabled()) { log.debug((++invokeCount) + ") invoking =>" + invocationReq + " with parameter: " + invocationReq.getParameter()); } //TOOD: -TME Need to find a better way of setting this // Need to set the local server locator so can make callbacks to this locator on server side. invocationReq.setLocator(localServerLocator); Marshaller marshaller = getMarshaller(); UnMarshaller unmarshaller = getUnMarshaller(); if (marshaller == null) { // try by locator (in case marshaller class name specified) marshaller = MarshalFactory.getMarshaller(getLocator(), getClassLoader()); if(marshaller == null) { // need to have a marshaller, so create a default one marshaller = MarshalFactory.getMarshaller(getDataType()); if(marshaller == null) { // went as far as possible to find a marshaller, will have to give up throw new InvalidMarshallingResource("Can not find a valid marshaller for data type: " + getDataType()); } } } if (unmarshaller == null) { // try by locator (in case unmarshaller class name specified) unmarshaller = MarshalFactory.getUnMarshaller(getLocator(), getClassLoader()); if(unmarshaller == null) { unmarshaller = MarshalFactory.getUnMarshaller(getDataType()); if(unmarshaller == null) { // went as far as possible to find a unmarshaller, will have to give up throw new InvalidMarshallingResource("Can not find a valid unmarshaller for data type: " + getDataType()); } } } try { // if raw, then send only param of invocation request Object payload = null; Map metadata = invocationReq.getRequestPayload(); if(metadata != null && metadata.get(Client.RAW) != null) { payload = invocationReq.getParameter(); } else { payload = invocationReq; } returnValue = transport(invocationReq.getSessionId(), payload, metadata, marshaller, unmarshaller); if (log.isDebugEnabled()) { log.debug("received result=>" + returnValue); } // Now check if is remoting response and process if(returnValue instanceof InvocationResponse) { InvocationResponse response = (InvocationResponse)returnValue; returnValue = response.getResult(); if(log.isDebugEnabled()) { log.debug("received result was an InvocationResponse so going to return response's return value of " + returnValue); log.debug("response is exception = " + response.isException()); } // if is a server side exception, throw it if(response.isException()) { throw (Throwable)returnValue; } } } catch (ClassNotFoundException cnf) { //TODO: -TME For now, just throw cnf, later will add dynamic classloading here. throw cnf; } return returnValue; } /** * this method is called prior to making the remote invocation to allow the subclass the ability * to provide additional data or modify the invocation * * @param sessionId * @param param * @param sendPayload * @param receivedPayload */ protected void preProcess(String sessionId, Object param, Map sendPayload, Map receivedPayload) { } /** * this method is called prior to returning the result for the invocation to allow the subclass the ability * to modify the result result * * @param sessionId * @param param * @param sendPayload * @param receivedPayload */ protected void postProcess(String sessionId, Object param, Map sendPayload, Map receivedPayload) { } /** * * @param sessionId * @param invocation * @param marshaller * @return * @throws IOException * @throws ConnectionFailedException */ protected abstract Object transport(String sessionId, Object invocation, Map metadata, Marshaller marshaller, UnMarshaller unmarshaller) throws IOException, ConnectionFailedException, ClassNotFoundException; /** * subclasses must provide this method to return true if their remote connection is connected and * false if disconnected. in some transports, such as SOAP, this method may always return true, since the * remote connectivity is done on demand and not kept persistent like other transports (such as socket-based * transport). * * @return boolean true if connected, false if not */ public boolean isConnected() { return connected; } /** * connect to the remote invoker * * @throws ConnectionFailedException */ public synchronized void connect() throws ConnectionFailedException { if (!connected) { if (log.isDebugEnabled()) { log.debug("connect called for: " + this); } handleConnect(); connected = true; } } /** * subclasses must implement this method to provide a hook to connect to the remote server, if this applies * to the specific transport. However, in some transport implementations, this may not make must difference since * the connection is not persistent among invocations, such as SOAP. In these cases, the method should * silently return without any processing. * * @throws ConnectionFailedException */ protected abstract void handleConnect() throws ConnectionFailedException; /** * subclasses must implement this method to provide a hook to disconnect from the remote server, if this applies * to the specific transport. However, in some transport implementations, this may not make must difference since * the connection is not persistent among invocations, such as SOAP. In these cases, the method should * silently return without any processing. */ protected abstract void handleDisconnect(); /** * disconnect from the remote invokere */ public synchronized void disconnect() { if (connected) { if (log.isDebugEnabled()) { log.debug("disconnect called for: " + this); } connected = false; handleDisconnect(); ClassLoader classLoader = getClassLoader(); if(classLoader != null && classLoader instanceof ClassByteClassLoader) { ((ClassByteClassLoader)classbyteloader).destroy(); } } } public void setMarshaller(Marshaller marshaller) { this.marshaller = marshaller; } public Marshaller getMarshaller() { return this.marshaller; } public void setUnMarshaller(UnMarshaller unmarshaller) { this.unmarshaller = unmarshaller; } public UnMarshaller getUnMarshaller() { return this.unmarshaller; } /** * Will get the data type for the marshaller factory so know which marshaller to * get to marshal the data. Will first check the locator uri for a 'datatype' * parameter and take that value if it exists. Otherwise, will use the * default datatype for the client invoker, based on transport. * * @return */ private String getDataType() { if (dataType == null) { dataType = getDataType(getLocator()); if (dataType == null) { dataType = getDefaultDataType(); } } return dataType; } private String getDataType(InvokerLocator locator) { String type = null; if (locator != null) { Map params = locator.getParameters(); if (params != null) { type = (String) params.get(InvokerLocator.DATATYPE); if(type == null) { type = (String) params.get(InvokerLocator.DATATYPE_CASED); } } } return type; } /** * Each implementation of the remote client invoker should have * a default data type that is uses in the case it is not specified * in the invoker locator uri. * * @return */ protected abstract String getDefaultDataType(); /** * Called by the garbage collector on an object when garbage collection * determines that there are no more references to the object. * A subclass overrides the <code>finalize</code> method to dispose of * system resources or to perform other cleanup. * <p/> * The general contract of <tt>finalize</tt> is that it is invoked * if and when the Java<font size="-2"><sup>TM</sup></font> virtual * machine has determined that there is no longer any * means by which this object can be accessed by any thread that has * not yet died, except as a result of an action taken by the * finalization of some other object or class which is ready to be * finalized. The <tt>finalize</tt> method may take any action, including * making this object available again to other threads; the usual purpose * of <tt>finalize</tt>, however, is to perform cleanup actions before * the object is irrevocably discarded. For example, the finalize method * for an object that represents an input/output connection might perform * explicit I/O transactions to break the connection before the object is * permanently discarded. * <p/> * The <tt>finalize</tt> method of class <tt>Object</tt> performs no * special action; it simply returns normally. Subclasses of * <tt>Object</tt> may override this definition. * <p/> * The Java programming language does not guarantee which thread will * transport the <tt>finalize</tt> method for any given object. It is * guaranteed, however, that the thread that invokes finalize will not * be holding any user-visible synchronization locks when finalize is * invoked. If an uncaught exception is thrown by the finalize method, * the exception is ignored and finalization of that object terminates. * <p/> * After the <tt>finalize</tt> method has been invoked for an object, no * further action is taken until the Java virtual machine has again * determined that there is no longer any means by which this object can * be accessed by any thread that has not yet died, including possible * actions by other objects or classes which are ready to be finalized, * at which point the object may be discarded. * <p/> * The <tt>finalize</tt> method is never invoked more than once by a Java * virtual machine for any given object. * <p/> * Any exception thrown by the <code>finalize</code> method causes * the finalization of this object to be halted, but is otherwise * ignored. * * @throws Throwable the <code>Exception</code> raised by this method */ protected void finalize() throws Throwable { disconnect(); super.finalize(); } }
RemoteClientInvoker.java |