Package org.jboss.remoting

Source Code of org.jboss.remoting.Client

/*
* 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.InputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.logging.Logger;
import org.jboss.remoting.callback.InvokerCallbackHandler;
import org.jboss.remoting.invocation.InternalInvocation;
import org.jboss.remoting.invocation.OnewayInvocation;
import org.jboss.remoting.marshal.Marshaller;
import org.jboss.remoting.marshal.UnMarshaller;
import org.jboss.remoting.stream.StreamServer;
import org.jboss.remoting.transport.ClientInvoker;
import org.jboss.util.id.GUID;
import org.jboss.util.threadpool.BasicThreadPool;
import org.jboss.util.threadpool.BlockingMode;
import org.jboss.util.threadpool.ThreadPool;

/**
* Client is a convience class for invoking remote methods for a given subsystem.
* It is intended to be the main user interface for making remote invocation
* on the client side.
*
* @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
* @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
* @version $Revision: 1.27 $
*/
public class Client implements Serializable
{
   /**
    * Key to be used when tracking callback listeners.
    */
   public static final String LISTENER_ID_KEY = "listenerId";

   /**
    * Specifies the default number of work threads in the pool for
    * executing one way invocations on the client.
    * Value is 10.
    */
   public static final int MAX_NUM_ONEWAY_THREADS = 10;

   /**
    * The key to use for the metadata Map passed when making a invoke() call
    * and wish for the invocation payload to be sent as is and not wrapped
    * within a remoting invocation request object.  This should be used
    * when want to make direct calls on systems outside of remoting
    * (e.g. making a http POST request to a web service).
    */
   public static final String RAW = "RAW_PAYLOAD";

   /**
    * Key for the configuration map passed to the Client constructor
    * to indicate that client should not make initial request to establish
    * lease with server.  May be needed when connecting to non-remoting server
    * (especially if using http client only).  The value for this should be
    * either a String that java.lang.Boolean can evaluate or a java.lang.Boolean.
    */
   public static final String ENABLE_LEASE = "ENABLE_LEASE";

   /**
    * Indicated the max number of threads used within oneway thread pool.
    */
   private int maxNumberThreads = MAX_NUM_ONEWAY_THREADS;
   private static final Logger log = Logger.getLogger(Client.class);
   private ClientInvoker invoker;
   private ClassLoader classloader;
   private String subsystem;
   private String sessionId = new GUID().toString();
   private ThreadPool onewayThreadPool;

   private ConnectionValidator connectionValidator = null;
   private LeasePinger leasePinger = null;
   private Map configuration = null;

   private boolean enableLease = false;
   private long leasePeriod = -1;

   /**
    * Constructs a remoting client with intended target server specified via the lcoator,
    *  without specifing a remote subsystem or including any metadata.
    * Same as calling Client(locator, null, null);
    * @param locator
    * @throws Exception
    */
   public Client(InvokerLocator locator) throws Exception
   {
      this(locator, null, null);
   }

   /**
    * Constructs a remoting client with intended target server specified via the locator
    * and configuration metadata.  The metadata supplied will be used when creating client
    * invoker (in the case specific data is required) and also for passing along additional
    * data to connection listeners on the server side in the case that the client fails, will
    * be able to use this extra information when notified.
    * @param locator
    * @param configuration
    * @throws Exception
    */
   public Client(InvokerLocator locator, Map configuration) throws Exception
   {
      this(locator, null, configuration);
   }

   /**
    * Constructs a remoting client with intended target server specified via the locator
    * and intended subsystem on server for invocations to be routed to.
    * @param locator
    * @param subsystem
    * @throws Exception
    */
   public Client(InvokerLocator locator, String subsystem)
         throws Exception
   {
      this(locator, subsystem, null);
   }

   /**
    * Constructs a remoting client with intended target server specified via the locator, intended subsystem
    * on the server for invocations to be routed to, and configuration metadata.
    * The metadata supplied will be used when creating client
    * invoker (in the case specific data is required) and also for passing along additional
    * data to connection listeners on the server side in the case that the client fails, will
    * be able to use this extra information when notified.
    * @param locator
    * @param subsystem
    * @param configuration
    * @throws Exception
    */
   public Client(InvokerLocator locator, String subsystem, Map configuration)
         throws Exception
   {
      this(Thread.currentThread().getContextClassLoader(), locator, subsystem, configuration);
   }

   /**
    * Constructs a remoting client with intended target server specified via the locator, intended subsystem
    * on the server for invocations to be routed to, and configuration metadata.
    * The metadata supplied will be used when creating client
    * invoker (in the case specific data is required) and also for passing along additional
    * data to connection listeners on the server side in the case that the client fails, will
    * be able to use this extra information when notified.
    * @param cl - the classloader that should be used by remoting
    * @param locator
    * @param subsystem
    * @param configuration
    * @throws Exception
    * @deprecated This constructor should not be used any more as will no longer take into
    * account the classloader specified as a parameter.
    */
   public Client(ClassLoader cl, InvokerLocator locator, String subsystem, Map configuration)
         throws Exception
   {
      this(cl, InvokerRegistry.createClientInvoker(locator, configuration), subsystem);
      this.configuration = configuration;
   }

   /**
    * Constructs a remoting client with intended target server specified via the locator
    * and intended subsystem on server for invocations to be routed to.
    * @param cl
    * @param invoker
    * @param subsystem
    * @throws Exception
    * @deprecated This constructor should not be used any more as will no longer take into
    * account the classloader specified as a parameter.
    */
   public Client(ClassLoader cl, ClientInvoker invoker, String subsystem)
         throws Exception
   {
      this.classloader = cl;
      this.subsystem = subsystem == null ? null : subsystem.toUpperCase();
      this.invoker = invoker;
   }

   /**
    * Adds a connection listener that will be notified if/when the connection
    * to the server fails while the client is idle (no calls being made).
    * The default behavior is to ping for connection every two seconds.
    *
    * @param listener
    */
   public void addConnectionListener(ConnectionListener listener)
   {
      if(connectionValidator == null)
      {
         connectionValidator = new ConnectionValidator(this);
      }
      connectionValidator.addConnectionListener(listener);
   }

   /**
    * Adds a connection listener that will be notified if/when the connection
    * to the server fails while the client is idle (no calls being made).
    * The current behavior is to ping the server periodically.  The time period
    * is defined by the pingPeriod (which should be in milliseconds).
    *
    * @param listener
    */
   public void addConnectionListener(ConnectionListener listener, int pingPeriod)
   {
      if(connectionValidator == null)
      {
         connectionValidator = new ConnectionValidator(this, pingPeriod);
      }
      connectionValidator.addConnectionListener(listener);
   }

   /**
    * Removes specified connection listener.  Will return true if it has
    * already been registered, false otherwise.
    *
    * @param listener
    * @return
    */
   public boolean removeConnectionListener(ConnectionListener listener)
   {
      return connectionValidator.removeConnectionListener(listener);
   }

   /**
    * This will set the session id used when making invocations on
    * server invokers.  There is a default unique id automatically
    * generated for each Client instance, so unless you have a good reason to set
    * this, do not set this.
    *
    * @param sessionId
    */
   public void setSessionId(String sessionId)
   {
      this.sessionId = sessionId;
   }

   /**
    * Gets the configuration map passed when constructing
    * this object.
    * @return
    */
   public Map getConfiguration()
   {
      return configuration;
   }

/**
    * Gets the session id used when making invocations on server invokers.
    * This is the id that will be used for tracking client connections on
    * the server side, to include client failures that are sent to
    * connection listeners on the server side.
    * @return
    */
   public String getSessionId()
   {
      return this.sessionId;
   }

   /**
    * Indicates if the underlying transport has been connected to
    * the target server.
    * @return
    */
   public boolean isConnected()
   {
      return (this.invoker != null && this.invoker.isConnected());
   }

   /**
    * Will cause the underlying transport to make connection to
    * the target server.  This is important for any stateful transports, like socket or multiplex.
    * This is also when a client lease with the server is started.
    * @throws Exception
    */
   public void connect() throws Exception
   {
      if(!isConnected())
      {
         connect(invoker);
      }
   }

   private void connect(ClientInvoker invoker)
   {
      invoker.connect();
      setupClientLease(invoker);
   }

   private void setupClientLease(ClientInvoker invoker)
   {
      // start with checking the locator url for hint as to if should do initial lease ping
      if(invoker != null)
      {
         InvokerLocator locator = invoker.getLocator();
         Map locatorParams = locator.getParameters();
         if(locatorParams != null)
         {
            String leaseValue = (String)locatorParams.get(InvokerLocator.CLIENT_LEASE);
            if(leaseValue != null && leaseValue.length() > 0)
            {
               enableLease = Boolean.valueOf(leaseValue).booleanValue();
            }
            String leasePeriodValue = (String)locatorParams.get(InvokerLocator.CLIENT_LEASE_PERIOD);
            if(leasePeriodValue != null && leasePeriodValue.length() > 0)
            {
               try
               {
                  leasePeriod = Long.parseLong(leasePeriodValue);
               }
               catch(NumberFormatException e)
               {
                  log.warn("Could not convert client lease period value (" + leasePeriodValue + ") to a number.");
               }
            }
         }
      }

      if(configuration != null)
      {
         Object val = configuration.get(ENABLE_LEASE);
         if(val != null)
         {
            if(val instanceof Boolean)
            {
               enableLease = ((Boolean) val).booleanValue();
            }
            else if(val instanceof String)
            {
               enableLease = Boolean.valueOf((String) val).booleanValue();
            }
            else
            {
               log.warn("Can not evaluate " + ENABLE_LEASE + " value (" + val + ") as a boolean type.");
            }
         }
      }

      if(enableLease)
      {
         Object ret = null;
         try
         {
            ret = invoker.invoke(new InvocationRequest(sessionId, subsystem, "$PING$", configuration, null, null));
            if(ret instanceof InvocationResponse)
            {
               InvocationResponse resp = (InvocationResponse) ret;
               Boolean shouldLease = (Boolean)resp.getResult();
               if(shouldLease.booleanValue())
               {

                  // if lease period not set via locator param, check value returned by server
                  if(leasePeriod < 0)
                  {
                     Map respMap = resp.getPayload();
                     if(respMap != null)
                     {
                        Long leaseTimeoutValue = (Long) respMap.get("clientLeasePeriod");
                        leasePeriod = leaseTimeoutValue.longValue();
                     }
                  }
                  if(leasePeriod > 0)
                  {
                     if(leasePinger == null)
                     {
                        leasePinger = new LeasePinger(this);
                        leasePinger.startPing(leasePeriod);
                     }
                  }
               }
            }
         }
         catch(Throwable throwable)
         {
            log.error("Error setting up client lease.", throwable);
         }
      }
   }

   /**
    * Disconnects the underlying transport from the target server.
    * Also notifies the target server to terminate client lease.  Is important
    * that this method is called when no longer using the remoting client.  Otherwise
    * resource will not be cleaned up and if the target server requires a lease, it
    * will be maintained in the background.
    */
   public void disconnect()
   {
      if(leasePinger != null)
      {
         try
         {
            invoker.invoke(new InvocationRequest(sessionId, subsystem, "$DISCONNECT$", null, null, null));
         }
         catch(Throwable throwable)
         {
            log.error("Error sending disconnect to server to end client lease.", throwable);
         }
         leasePinger.stopPing();
      }
      this.invoker.disconnect();
   }

   /**
    * Get the client invoker (transport implementation).
    * @return
    */
   public ClientInvoker getInvoker()
   {
      return invoker;
   }

   /**
    * Set the client invoker (transport implementation)
    * @param invoker
    */
   public void setInvoker(ClientInvoker invoker)
   {
      this.invoker = invoker;
   }

   /**
    * Gets the subsystem being used when routing
    * invocation request on the server side.
    * @return
    */
   public String getSubsystem()
   {
      return subsystem;
   }

   /**
    * Sets the subsystem being used when routing invocation requests
    * on the server side.  Specifing a subsystem is only needed when
    * server has multiple handlers registered (which will each have their
    * own associated subsystem).
    * @param subsystem
    */
   public void setSubsystem(String subsystem)
   {
      this.subsystem = subsystem;
   }

   /**
    * Invokes the server invoker handler with the payload parameter passed.
    * Same as calling invoke(param, null);
    * @param param
    * @return
    * @throws Throwable
    */
   public Object invoke(Object param) throws Throwable
   {
      return invoke(param, null);
   }

   /**
    * invoke the method remotely
    *
    * @param param    - payload for the server invoker handler
    * @param metadata - any extra metadata that may be needed by the transport (i.e. GET or POST if using
    *                 http invoker) or if need to pass along extra data to the server invoker handler.
    * @return
    * @throws Throwable
    */
   public Object invoke(Object param, Map metadata)
         throws Throwable
   {
      return invoke(param, metadata, null);
   }

   private Object invoke(Object param, Map metadata, InvokerLocator callbackServerLocator)
         throws Throwable
   {
      /**
       * Using a local variable for the invoker as work around so don't have
       * to sync method (and take performance hit)
       * Although this may cause having multiple instances of invoker in existance at
       * one time, should avoid having reference changed by another thread while in
       * execution path for method.
       */
      ClientInvoker localInvoker = invoker;

      if(localInvoker != null)
      {
         /**
          * This is here due to way the InvokerRegistry works.  Since it will cache references to
          * client invokers it creates based on locator, it is possible that this instance will
          * have a reference to the same instance of the invoker as another client.  So is possible
          * that client will disconnect, which will cause the invoker to be disconnected.  Therefore,
          * if this were to happen, we would create a new one by calling the createClientInvoker() method.
          */
         if(localInvoker.isConnected() == false)
         {
            log.debug("invoke called, but our invoker is disconnected, discarding and fetching another fresh invoker for: " + invoker.getLocator());

            localInvoker = InvokerRegistry.createClientInvoker(localInvoker.getLocator(), configuration);
            connect(localInvoker);
         }
      }
      else
      {
         throw new Exception("Can not perform invoke because invoker is null.");
      }

      Object ret = localInvoker.invoke(new InvocationRequest(sessionId, subsystem, param, metadata, null, callbackServerLocator));
      this.invoker = localInvoker;
      return ret;
   }

   /**
    * Will invoke a oneway call to server without a return object.  This should be used when not expecting a
    * return value from the server and wish to achieve higher performance, since the client will not wait for
    * a return.
    * <b>
    * This is done one of two ways.  The first is to pass true as the clientSide param.  This will cause the
    * execution of the remote call to be excuted in a new thread on the client side and will return the calling thread
    * before making call to server side.  Although, this is optimal for performance, will not know about any problems
    * contacting server.
    * <p/>
    * The second, is to pass false as the clientSide param.  This will allow the current calling thread to make
    * the call to the remote server, at which point, the server side processing of the thread will be executed on
    * the remote server in a new executing thread and the client thread will return.  This is a little slower, but
    * will know that the call made it to the server.
    *
    * @param param
    * @param sendPayload
    * @param clientSide
    */
   public void invokeOneway(final Object param, final Map sendPayload, boolean clientSide) throws Throwable
   {
      if(clientSide)
      {
         ThreadPool threadPool = getOnewayThreadPool();
         Runnable onewayRun = new Runnable()
         {
            public void run()
            {
               try
               {
                  invoke(param, sendPayload);
               }
               catch(Throwable e)
               {
                  // throw away exception since can't get it back to original caller
                  log.error("Error executing client oneway invocation request: " + param, e);
               }
            }
         };
         threadPool.run(onewayRun);
      }
      else
      {
         OnewayInvocation invocation = new OnewayInvocation(param);
         invoke(invocation, sendPayload);
      }
   }

   /**
    * Sets the maximum number of threads to use within client pool for
    * one way invocations on the client side (meaning oneway invocation
    * is handled by thread in this pool and user's call returns immediately)
    * Default value is MAX_NUM_ONEWAY_THREADS.
    * @param numOfThreads
    */
   public void setMaxNumberOfThreads(int numOfThreads)
   {
      this.maxNumberThreads = numOfThreads;
   }

   /**
    * Gets the maximum number of threads to use within client pool for
    * one way invocations on the client side (meaning oneway invocation
    * is handled by thread in this pool and user's call returns immediately)
    * Default value is MAX_NUM_ONEWAY_THREADS.
    * @return
    */
   public int getMaxNumberOfThreads()
   {
      return this.maxNumberThreads;
   }

   /**
    * Gets the thread pool being used for making
    * one way invocations on the client side.
    * If one has not be specifically set via configuration
    * or call to set it, will always return instance of
    * org.jboss.util.threadpool.BasicThreadPool.
    * @return
    */
   public ThreadPool getOnewayThreadPool()
   {
      if(onewayThreadPool == null)
      {
         BasicThreadPool pool = new BasicThreadPool("JBossRemoting Client Oneway");
         pool.setMaximumPoolSize(maxNumberThreads);
         pool.setBlockingMode(BlockingMode.WAIT);
         onewayThreadPool = pool;
      }
      return onewayThreadPool;
   }

   /**
    * Sets the thread pool to be used for making
    * one way invocations on the client side.
    * @param pool
    */
   public void setOnewayThreadPool(ThreadPool pool)
   {
      this.onewayThreadPool = pool;
   }

   /**
    * Same as calling invokeOneway(Object param, Map sendPayload, boolean clientSide) with
    * clientSide param being false and a null sendPayload.  Therefore, client thread will not return till it has made
    * remote call.
    *
    * @param param
    */
   public void invokeOneway(Object param) throws Throwable
   {
      invokeOneway(param, null);
   }

   /**
    * Same as calling invokeOneway(Object param, Map sendPayload, boolean clientSide) with
    * clientSide param being false.  Therefore, client thread will not return till it has made
    * remote call.
    *
    * @param param
    * @param sendPayload
    */
   public void invokeOneway(Object param, Map sendPayload) throws Throwable
   {
      invokeOneway(param, sendPayload, false);
   }

   /**
    * Adds the specified handler as a callback listener for pull (sync) callbacks.
    * The invoker server will then collect the callbacks for this specific handler.
    * The callbacks can be retrieved by calling the getCallbacks() method.
    * Note: this will cause the client invoker's client locator to be set to null.
    *
    * @param callbackHandler
    * @throws Throwable
    */
   public void addListener(InvokerCallbackHandler callbackHandler) throws Throwable
   {
      addListener(callbackHandler, null);
   }

   /**
    * Adds the specified handler as a callback listener for push (async) callbacks.
    * The invoker server will then callback on this handler (via the server invoker
    * specified by the clientLocator) when it gets a callback from the server handler.
    * Note: passing a null clientLocator will cause the client invoker's client
    * locator to be set to null.
    *
    * @param callbackHandler
    * @param clientLocator
    * @throws Throwable
    */
   public void addListener(InvokerCallbackHandler callbackHandler,
                           InvokerLocator clientLocator) throws Throwable
   {
      addListener(callbackHandler, clientLocator, null);
   }

   /**
    * Adds the specified handler as a callback listener for push (async) callbacks.
    * The invoker server will then callback on this handler (via the server invoker
    * specified by the clientLocator) when it gets a callback from the server handler.
    * Note: passing a null clientLocator will cause the client invoker's client
    * locator to be set to null.
    *
    * @param callbackHandler       interface to call on with callback
    * @param clientLocator         locator for callback server to callback on
    * @param callbackHandlerObject will be included in the callback object passed upon callback
    * @throws Throwable
    */
   public void addListener(InvokerCallbackHandler callbackHandler,
                           InvokerLocator clientLocator, Object callbackHandlerObject) throws Throwable
   {
      if(callbackHandler != null)
      {
         Map metadata = createListenerMetadata(callbackHandler);
         String listenerId = (String) metadata.get(LISTENER_ID_KEY);
         invoker.addClientLocator(listenerId, clientLocator);
         if(clientLocator != null)
         {
            Client client = new Client(clientLocator, subsystem);
            client.setSessionId(getSessionId());
            client.connect();

            client.invoke(new InternalInvocation(InternalInvocation.ADDCLIENTLISTENER,
                                                 new Object[]{callbackHandler, callbackHandlerObject}),
                          metadata);
            client.disconnect();
         }
         // now call server to add listener
         invoke(new InternalInvocation(InternalInvocation.ADDLISTENER, null), metadata, clientLocator);
      }
      else
      {
         throw new NullPointerException("InvokerCallbackHandler to be added as a listener can not be null.");
      }
   }

   private Map createListenerMetadata(InvokerCallbackHandler callbackHandler)
   {
      String listenerId = String.valueOf(callbackHandler.hashCode());
      Map metadata = new HashMap();
      metadata.put(LISTENER_ID_KEY, listenerId);
      return metadata;
   }

   /**
    * Removes callback handler as a callback listener from the server (and client in
    * the case that it was setup to receive async callbacks). See addListener().
    *
    * @param callbackHandler
    * @throws Throwable
    */
   public void removeListener(InvokerCallbackHandler callbackHandler) throws Throwable
   {
      if(callbackHandler != null)
      {
         Map metadata = createListenerMetadata(callbackHandler);
         String listenerId = (String) metadata.get(LISTENER_ID_KEY);
         // connect to the given client locator and remove handler as listener
         InvokerLocator locator = invoker.getClientLocator(listenerId);
         if(locator != null) // async callback
         {
            Client client = new Client(locator, subsystem);
            client.setSessionId(getSessionId());
            client.connect();
            client.invoke(new InternalInvocation(InternalInvocation.REMOVECLIENTLISTENER,
                                                 new Object[]{callbackHandler}),
                          metadata);
            client.disconnect();
         }
         // now call server to remove listener
         invoke(new InternalInvocation(InternalInvocation.REMOVELISTENER, null), metadata);
      }
      else
      {
         throw new NullPointerException("Can not remove null InvokerCallbackHandler listener.");
      }
   }

   /**
    * Gets the callbacks for specified callback handler.  The handler is required because an id is generated
    * for each handler.  So if have two callback handlers registered with the same server, no other way to know
    * for which handler to get the callbacks for.
    *
    * @param callbackHandler
    * @return
    * @throws Throwable
    */
   public List getCallbacks(InvokerCallbackHandler callbackHandler) throws Throwable
   {
      if(callbackHandler != null)
      {
         Map metadata = createListenerMetadata(callbackHandler);
         return (List) invoke(new InternalInvocation(InternalInvocation.GETCALLBACKS, null), metadata);
      }
      else
      {
         throw new NullPointerException("Can not remove null InvokerCallbackHandler listener.");
      }
   }

   /**
    * Sets the marshaller implementation that should be used by the
    * client invoker (transport).  This overrides the client's default
    * marshaller (or any set within configuration).
    * @param marshaller
    */
   public void setMarshaller(Marshaller marshaller)
   {
      if(invoker != null && marshaller != null)
      {
         invoker.setMarshaller(marshaller);
      }
   }

   /**
    * Sets the unmarshaller implementation that should be used
    * by the client invoker (transport).  This overrides the client's default
    * unmarshaller (or any set within configuration).
    * @param unmarshaller
    */
   public void setUnMarshaller(UnMarshaller unmarshaller)
   {
      if(invoker != null && unmarshaller != null)
      {
         invoker.setUnMarshaller(unmarshaller);
      }
   }

   /**
    * Takes an inputstream and wraps a server around.  Then calls the target
    * remoting server and passes a proxy for an inputstream to the server's handler.
    * When the server handler calls on this proxy, it will call back on this server
    * wrapped around this inputstream.
    *
    * @param inputStream
    * @param param       invocation payload
    * @return the return value from the invocation
    * @throws Throwable
    */
   public Object invoke(InputStream inputStream, Object param) throws Throwable
   {
      StreamServer streamServer = new StreamServer(inputStream);
      String locator = streamServer.getInvokerLocator();

      // now call on target server and pass locator for stream callbacks
      return invoke(new InternalInvocation(InternalInvocation.ADDSTREAMCALLBACK, new Object[]{locator, param}), null);
   }
}
TOP

Related Classes of org.jboss.remoting.Client

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.