Package org.jboss.remoting

Source Code of org.jboss.remoting.RemoteClientInvoker

/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.jboss.remoting;

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

import javax.net.SocketFactory;

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.remoting.util.socket.RemotingSSLSocketFactory;

/**
* 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.15.4.3 $
*/
public abstract class RemoteClientInvoker extends AbstractInvoker implements ClientInvoker
{
   protected boolean connected = false;
   private Marshaller marshaller;
   private UnMarshaller unmarshaller;
   private String dataType;

   // SocketFactory for use by derived transports
   private SocketFactory socketFactory;

   public RemoteClientInvoker(InvokerLocator locator)
   {
      super(locator);
   }

   public RemoteClientInvoker(InvokerLocator locator, Map configuration)
   {
      super(locator, configuration);
      socketFactory = createSocketFactory(configuration);
   }
  
   /**
    * 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.isTraceEnabled())
      {
         log.trace((++invokeCount) + ") invoking =>" + invocationReq + " with parameter: " + invocationReq.getParameter());
      }

      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(), 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());
            }
         }
      }

      // 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.
      ClassLoader remotingClassLoader = new RemotingClassLoader(getClassLoader(),
                                                                Thread.currentThread().getContextClassLoader());

      if(unmarshaller == null)
      {
         // try by locator (in case unmarshaller class name specified)
         unmarshaller = MarshalFactory.getUnMarshaller(getLocator(), getClassLoader());
         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 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.isTraceEnabled())
      {
         log.trace("received result=>" + returnValue);
      }

      // Now check if is remoting response and process
      if(returnValue instanceof InvocationResponse)
      {
         InvocationResponse response = (InvocationResponse) returnValue;
         returnValue = response.getResult();
         if(log.isTraceEnabled())
         {
            log.trace("received result was an InvocationResponse so going to return response's return value of " + returnValue);
            log.trace("response is exception = " + response.isException());
         }
         // if is a server side exception, throw it
         if(response.isException())
         {
            Throwable e = (Throwable) returnValue;

            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);

            if(e.getCause() != null)
            {
               e.getCause().setStackTrace(completeStackTrace);
            }
            else
            {
               e.setStackTrace(completeStackTrace);
            }

            throw e;
         }
      }

      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)
      {
         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)
      {
         log.debug("disconnect called for: " + this);

         connected = false;
         handleDisconnect();
         ClassLoader classLoader = getClassLoader();
         if(classLoader != null && classLoader instanceof ClassByteClassLoader)
         {
            ((ClassByteClassLoader) classbyteloader).destroy();
         }
      }
      /**
       * Need to remove myself from registry so will not keep
       * reference to me since I am of no use now. Will have to create
       * a new one.
       */
      InvokerRegistry.destroyClientInvoker(getLocator());
   }

   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 SocketFactory getSocketFactory()
   {
      return socketFactory;
   }

   /**
    * 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();
   }

   /**
    * If any configuration parameters relate to the construction of a RemotingSSLSocketFactory,
    * create one.
    *
    * @param configuration
    * @return
    */
   protected SocketFactory createSocketFactory(Map configuration)
   {
      if (configuration == null)
         return null;

      if (configuration.get(RemotingSSLSocketFactory.REMOTING_ALGORITHM) != null ||
          configuration.get(RemotingSSLSocketFactory.REMOTING_KEY_ALIAS) != null ||
          configuration.get(RemotingSSLSocketFactory.REMOTING_KEY_STORE_FILE_PATH) != null ||
          configuration.get(RemotingSSLSocketFactory.REMOTING_KEY_STORE_PASSWORD) != null ||
          configuration.get(RemotingSSLSocketFactory.REMOTING_KEY_STORE_TYPE) != null ||
          configuration.get(RemotingSSLSocketFactory.REMOTING_TRUST_ALGORITHM) != null ||
          configuration.get(RemotingSSLSocketFactory.REMOTING_TRUST_STORE_FILE_PATH) != null ||
          configuration.get(RemotingSSLSocketFactory.REMOTING_TRUST_STORE_PASSWORD) != null ||
          configuration.get(RemotingSSLSocketFactory.REMOTING_TRUST_STORE_TYPE) != null
      )
         return new RemotingSSLSocketFactory(configuration);
      else
         return null;
}
}
TOP

Related Classes of org.jboss.remoting.RemoteClientInvoker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.