All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.remoting.MicroRemoteClientInvoker Maven / Gradle / Ivy

There is a newer version: 5.0.29.Final
Show newest version
package org.jboss.remoting;

import org.jboss.logging.Logger;
import org.jboss.remoting.loading.ClassByteClassLoader;
import org.jboss.remoting.loading.RemotingClassLoader;
import org.jboss.remoting.marshal.InvalidMarshallingResource;
import org.jboss.remoting.marshal.MarshalFactory;
import org.jboss.remoting.marshal.Marshaller;
import org.jboss.remoting.marshal.UnMarshaller;
import org.jboss.remoting.transport.ClientInvoker;
import org.jboss.util.id.GUID;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * MicroRemoteClientInvoker 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. 

*

* 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. * * The reason for the name micro is that this class contains only api that can be run within a J2ME envrionment. * * @author Jeff Haynie * @author Tom Elrod * @version $Revision: 6226 $ */ public abstract class MicroRemoteClientInvoker extends AbstractInvoker implements ClientInvoker { private static final Logger log = Logger.getLogger(MicroRemoteClientInvoker.class); private static final String LEASE_CREATION_TIMEOUT_DEFAULT = "30000"; private boolean trace = log.isTraceEnabled(); protected boolean connected = false; private Marshaller marshaller; private UnMarshaller unmarshaller; private String dataType; private final Object clientLeaseLock = new Object(); private LeasePinger leasePinger = null; private String invokerSessionID = new GUID().toString(); private boolean parentFirstClassLoading = true; private boolean changeInvalidStateToCannotConnect = false; private boolean useCurrentThreadClassLoader; private String leaseCreationTimeout = LEASE_CREATION_TIMEOUT_DEFAULT; public MicroRemoteClientInvoker(InvokerLocator locator) { super(locator); init(); } public MicroRemoteClientInvoker(InvokerLocator locator, Map configuration) { super(locator, configuration); init(); } /** * Transport a request against a remote ServerInvoker. */ public Object invoke(InvocationRequest invocationReq) throws Throwable { Object returnValue = null; int invokeCount = 0; if (trace) { log.trace(this + "(" + (++invokeCount) + ") invoking " + invocationReq); } Marshaller marshaller = getMarshaller(); UnMarshaller unmarshaller = getUnMarshaller(); if (marshaller == null) { // try by locator (in case marshaller class name specified) Map map = passConfigMapToMarshalFactory ? configuration : null; marshaller = MarshalFactory.getMarshaller(getLocator(), getClassLoader(), map); if (marshaller == null) { // need to have a marshaller, so create a default one marshaller = MarshalFactory.getMarshaller(getDataType(), getSerializationType()); 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()); } setMarshaller(marshaller); } } if (unmarshaller == null) { // creating a new classloader containing the remoting class loader (for remote classloading) // and the current thread's class loader. This allows to load remoting classes as well as // user's classes. RemotingClassLoader remotingClassLoader = null; if (parentFirstClassLoading) { remotingClassLoader = new RemotingClassLoader(getClassLoader(), Thread.currentThread().getContextClassLoader()); } else { remotingClassLoader = new RemotingClassLoader(Thread.currentThread().getContextClassLoader(), getClassLoader()); } remotingClassLoader.setUseCurrentThreadClassLoader(useCurrentThreadClassLoader); // try by locator (in case unmarshaller class name specified) Map map = passConfigMapToMarshalFactory ? configuration : null; unmarshaller = MarshalFactory.getUnMarshaller(getLocator(), getClassLoader(), map); if (unmarshaller == null) { unmarshaller = MarshalFactory.getUnMarshaller(getDataType(), getSerializationType()); 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()); } } unmarshaller.setClassLoader(remotingClassLoader); if (!useCurrentThreadClassLoader) { setUnMarshaller(unmarshaller); } } // 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); // Now check if is remoting response and process if (returnValue instanceof InvocationResponse) { InvocationResponse response = (InvocationResponse)returnValue; returnValue = response.getResult(); // if is a server side exception, throw it if (response.isException()) { Throwable e = (Throwable)returnValue; if (trace) { log.trace(this + " received a server-side exception as response to the invocation: " + e); } StackTraceElement[] serverStackTrace; if (e.getCause() != null) { serverStackTrace = e.getCause().getStackTrace(); if (serverStackTrace == null || serverStackTrace.length == 0) { serverStackTrace = e.getStackTrace(); } } else { serverStackTrace = e.getStackTrace(); } // need to check that there is a server stack trace. If there is not, need to log // warning here so caller knows that error happened on server side and to look there, // as stack trace is just going to lead them to here, giving the impression that is // a client side exception from this point within remoting client. if (serverStackTrace == null || serverStackTrace.length == 0) { log.warn("An exception occurred on the server side when making remote invocation. " + "The exception returned from server does not include a stack trace. " + "Original server side exception message is " + e.getMessage(), e); } Exception clientException = new Exception(); StackTraceElement[] clientStackTrace = clientException.getStackTrace(); StackTraceElement[] completeStackTrace = new StackTraceElement[serverStackTrace.length + clientStackTrace.length]; System.arraycopy(serverStackTrace, 0, completeStackTrace, 0, serverStackTrace.length); System.arraycopy(clientStackTrace, 0, completeStackTrace, serverStackTrace.length, clientStackTrace.length); Throwable responseException = null; if (e instanceof ServerInvoker.InvalidStateException && changeInvalidStateToCannotConnect) { responseException = new CannotConnectException(e.getMessage(), e.getCause()); } else { responseException = e; } if (e.getCause() != null) { responseException.getCause().setStackTrace(completeStackTrace); } else { responseException.setStackTrace(completeStackTrace); } throw responseException; } if (trace) { log.trace(this + " received InvocationResponse so going to return response's return value of " + returnValue);} } 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) { } 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. */ public synchronized void connect() throws ConnectionFailedException { if (!connected) { log.debug(this + " connecting"); handleConnect(); connected = true; log.debug(this + " connected"); } } /** * 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 (trace) { log.trace(this + " disconnecting ..."); } if (connected) { connected = false; handleDisconnect(); ClassLoader classLoader = getClassLoader(); if (classLoader != null && classLoader instanceof ClassByteClassLoader) { ((ClassByteClassLoader) classbyteloader).destroy(); } if (trace) { log.trace(this + " disconnected"); } } else { if (trace) { log.trace(this + " is not connected!"); } } } 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; } public String getSessionId() { return this.invokerSessionID; } public String getLeaseCreationTimeout() { return leaseCreationTimeout; } public void setLeaseCreationTimeout(String leaseCreationTimeout) { this.leaseCreationTimeout = leaseCreationTimeout; } public void terminateLease(String sessionId, int disconnectTimeout) { terminateLease(sessionId, disconnectTimeout, null); } public void terminateLease(String sessionId, int disconnectTimeout, LeasePinger passedLeasePinger) { synchronized(clientLeaseLock) { if (passedLeasePinger != null && passedLeasePinger != leasePinger) { if (trace) log.trace(this + ": " + passedLeasePinger + " != " + leasePinger); return; } if (trace) log.trace(this + " entering terminateLease() for " + leasePinger); if(leasePinger != null) { if (disconnectTimeout == Client.DEFAULT_DISCONNECT_TIMEOUT) { disconnectTimeout = Integer.parseInt(leaseCreationTimeout); } leasePinger.setDisconnectTimeout(disconnectTimeout); if (sessionId == null) { if (trace) log.trace(this + " shutting down LeasePinger: " + leasePinger); // Independent of any particular Client - force LeasePinger shutdown. // Should be called only if there is a reasonable belief that the lease // has already stopped on the server side. try { leasePinger.stopPing(); } catch (Exception e) { log.debug("error shutting down lease pinger" + e.getMessage()); log.trace("error shutting down lease pinger", e); } leasePinger = null; } else { // Remove a particular Client. if (trace) log.trace(this + " removing client " + sessionId + " from LeasePinger: " + leasePinger); boolean isLastClientLease = leasePinger.removeClient(sessionId); if(isLastClientLease) { if (trace) log.trace(this + " shutting down LeasePinger, " + sessionId + " was last client lease: " + leasePinger); try { leasePinger.stopPing(); } catch (Exception e) { log.debug("error shutting down lease pinger"); } leasePinger = null; } } } else { if (trace) log.trace(this + " leasePinger is null: must have been shut down already"); } if (trace) log.trace(this + " leaving terminateLease() for " + leasePinger); } } public long getLeasePeriod(String sessionID) { synchronized(clientLeaseLock) { if(leasePinger == null) { return -1; } return leasePinger.getLeasePeriod(sessionID); } } public void establishLease(String clientSessionID, Map configuration, long leasePeriod) throws Throwable { Client client = (Client) configuration.get(Client.CLIENT); ConnectionListener listener = (ConnectionListener) configuration.remove(Client.CONNECTION_LISTENER); boolean useClientConnectionIdentity = false; if (configuration != null) { Object o = configuration.get(Remoting.USE_CLIENT_CONNECTION_IDENTITY); if (o instanceof String) { useClientConnectionIdentity = Boolean.valueOf((String) o).booleanValue(); } else if (o != null) { log.warn("value of " + Remoting.USE_CLIENT_CONNECTION_IDENTITY + " must be a String: " + o); } } synchronized (clientLeaseLock) { // if already have a lease pinger, then already have a client with an established // lease and just need to update the lease pinger if (leasePinger != null) { try { leasePinger.addClient(clientSessionID, configuration, leasePeriod); } catch (RuntimeException e) { throw e.getCause(); } if (trace) log.trace(this + " added client with session ID " + clientSessionID + " to " + leasePinger); } else { try { if(trace) { log.trace(this + " sending initial lease ping to server to determine if server has leasing enabled."); } // configuration should NOT be passed as want ping to be specific to client invoker // and NOT to the client. String leasePingerId = new GUID().toString(); Map requestMap = new HashMap(); requestMap.put(LeasePinger.LEASE_PINGER_ID, leasePingerId); requestMap.put(LeasePinger.TIME_STAMP, Long.toString(System.currentTimeMillis())); requestMap.put(ServerInvoker.TIMEOUT, leaseCreationTimeout); if (trace) log.trace(this + " initiating lease for leasePingerId " + leasePingerId); InvocationRequest ir = new InvocationRequest(invokerSessionID, null, "$PING$", requestMap, new HashMap(), null); Object ret = invoke(ir); if (ret instanceof InvocationResponse) { InvocationResponse resp = (InvocationResponse) ret; Boolean shouldLease = (Boolean)resp.getResult(); if (shouldLease.booleanValue()) { long defaultLeasePeriod = LeasePinger.DEFAULT_LEASE_PERIOD; Map respMap = resp.getPayload(); if (respMap != null) { Long leaseTimeoutValue = (Long)respMap.get("clientLeasePeriod"); long serverDefaultLeasePeriod = leaseTimeoutValue.longValue(); if(serverDefaultLeasePeriod > 0) { defaultLeasePeriod = serverDefaultLeasePeriod; } } if(trace) { log.trace("server does have leasing enabled (with default lease period of " + defaultLeasePeriod + ") and will start a new lease pinger."); } leasePinger = new LeasePinger(this, invokerSessionID, defaultLeasePeriod); leasePinger.setLeasePingerId(leasePingerId); leasePinger.setUseClientConnectionIdentity(useClientConnectionIdentity); leasePinger.addClient(clientSessionID, configuration, leasePeriod); leasePinger.startPing(); } } } catch (Throwable throwable) { Exception e = new Exception("Error setting up client lease"); e.initCause(throwable); throw e; } } if (trace) log.trace(this + ": client = " + client + ", listener = " + listener); if (client != null && listener != null) { client.addConnectionListener(listener, configuration); } } } /** * 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. */ private String getDataType() { if (dataType == null) { String localDataType = getDataType(getLocator()); if (localDataType == null) { localDataType = getDefaultDataType(); } dataType = localDataType; } 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; } protected void init() { // Get the parent delegation order flag, default is parent first Object flag = configuration.get(Remoting.CLASSLOADING_PARENT_FIRST_DELEGATION); if(flag == null) { // Fallback to the system property flag = System.getProperty(Remoting.CLASSLOADING_PARENT_FIRST_DELEGATION_PROP); } boolean parentFirst = true; if (flag != null) { String sflag = flag.toString(); parentFirst = Boolean.valueOf(sflag).booleanValue(); } parentFirstClassLoading = parentFirst; flag = configuration.get(Remoting.CHANGE_INVALID_STATE_TO_CANNOT_CONNECT); if (flag != null) { String sflag = flag.toString(); changeInvalidStateToCannotConnect = Boolean.valueOf(sflag).booleanValue(); } flag = configuration.get(Remoting.USE_CURRENT_THREAD_CLASS_LOADER); if (flag != null) { String sflag = flag.toString(); useCurrentThreadClassLoader = Boolean.valueOf(sflag).booleanValue(); } } /** * 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. */ 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 finalize method to dispose of * system resources or to perform other cleanup. *

* The general contract of finalize is that it is invoked * if and when the JavaTM 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 finalize method may take any action, including * making this object available again to other threads; the usual purpose * of finalize, 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. *

* The finalize method of class Object performs no * special action; it simply returns normally. Subclasses of * Object may override this definition. *

* The Java programming language does not guarantee which thread will * transport the finalize 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. *

* After the finalize 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. *

* The finalize method is never invoked more than once by a Java * virtual machine for any given object. *

* Any exception thrown by the finalize method causes * the finalization of this object to be halted, but is otherwise * ignored. * * @throws Throwable the Exception raised by this method */ protected void finalize() throws Throwable { disconnect(); super.finalize(); } protected LeasePinger getLeasePinger() { return leasePinger; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy