Package org.jboss.remoting.transport.socket

Source Code of org.jboss.remoting.transport.socket.SocketClientInvoker

/*
* 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.transport.socket;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.rmi.MarshalException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import org.jboss.remoting.CannotConnectException;
import org.jboss.remoting.ConnectionFailedException;
import org.jboss.remoting.InvokerLocator;
import org.jboss.remoting.RemoteClientInvoker;
import org.jboss.remoting.marshal.Marshaller;
import org.jboss.remoting.marshal.UnMarshaller;
import org.jboss.remoting.marshal.serializable.SerializableMarshaller;

import javax.net.SocketFactory;

/**
* SocketClientInvoker uses Sockets to remotely connect to the a remote ServerInvoker, which
* must be a SocketServerInvoker.
*
* @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
* @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
* @version $Revision: 1.30.2.1 $
*/
public class SocketClientInvoker extends RemoteClientInvoker
{
   private InetAddress addr;
   private int port;

   public static final String TCP_NODELAY_FLAG = "enableTcpNoDelay";
   public static final String MAX_POOL_SIZE_FLAG = "clientMaxPoolSize";
   public static final String SO_TIMEOUT_FLAG = "timeout";
   public static final String CLIENT_SOCKET_CLASS_FLAG = "clientSocketClass";

   /**
    * Default value for socket timeout is 30 minutes.
    */
   public static final int SO_TIMEOUT_DEFAULT = 1800000;
   public static final boolean TCP_NODELAY_DEFAULT = false;

   private boolean shouldCheckConnection = true;

   // Performance measurements
   public static long getSocketTime = 0;
   public static long readTime = 0;
   public static long writeTime = 0;
   public static long serializeTime = 0;
   public static long deserializeTime = 0;

   /**
    * If the TcpNoDelay option should be used on the socket.
    */
   protected boolean enableTcpNoDelay = TCP_NODELAY_DEFAULT;

   protected int timeout = SO_TIMEOUT_DEFAULT;

   protected String clientSocketClassName = ClientSocketWrapper.class.getName();
   private Constructor clientSocketConstructor = null;

   /**
    * Set number of retries in getSocket method
    */
   public static final int MAX_RETRIES = 3;
   public static long usedPooled = 0;

   protected int numberOfRetries = MAX_RETRIES;

   /**
    * Pool for this invoker.  This is shared between all
    * instances of proxies attached to a specific invoker
    */
   protected LinkedList pool = null;

   /**
    * connection information
    */
   protected ServerAddress address;

   protected HashMap connectionPools = new HashMap();
   protected int maxPoolSize = 10;

   public SocketClientInvoker(InvokerLocator locator)
   throws IOException
   {
      this(locator, null);
   }

   public SocketClientInvoker(InvokerLocator locator, Map configuration)
         throws IOException
   {
      super(locator, configuration);
      try
      {
         setup();
      }
      catch(Exception ex)
      {
         throw new RuntimeException(ex.getMessage());
      }
   }

   protected void setup()
         throws Exception
   {
      this.addr = InetAddress.getByName(locator.getHost());
      this.port = locator.getPort();

      configureParameters();

      address = new ServerAddress(addr.getHostAddress(), port, enableTcpNoDelay, timeout);
   }

   protected void configureParameters()
   {
      Map params = configuration;
      if(params != null)
      {
         // look for enableTcpNoDelay param
         Object val = params.get(TCP_NODELAY_FLAG);
         if(val != null)
         {
            try
            {
               boolean bVal = Boolean.valueOf((String) val).booleanValue();
               enableTcpNoDelay = bVal;
               log.debug("Setting SocketClientInvoker::enableTcpNoDelay to: " + enableTcpNoDelay);
            }
            catch(Exception e)
            {
               log.warn("Could not convert " + TCP_NODELAY_FLAG + " value of " + val + " to a boolean value.");
            }
         }
         // look for maxPoolSize param
         val = params.get(MAX_POOL_SIZE_FLAG);
         if(val != null)
         {
            try
            {
               int nVal = Integer.valueOf((String) val).intValue();
               maxPoolSize = nVal;
               log.debug("Setting SocketClientInvoker::maxPoolSize to: " + maxPoolSize);
            }
            catch(Exception e)
            {
               log.warn("Could not convert " + MAX_POOL_SIZE_FLAG + " value of " + val + " to a int value.");
            }
         }
         // look for socketTimeout param
         val = params.get(SO_TIMEOUT_FLAG);
         if(val != null)
         {
            try
            {
               int nVal = Integer.valueOf((String) val).intValue();
               timeout = nVal;
               log.debug("Setting SocketClientInvoker::timeout to: " + timeout);
            }
            catch(Exception e)
            {
               log.warn("Could not convert " + SO_TIMEOUT_FLAG + " value of " + val + " to a int value.");
            }
         }
         // look for client socket class name
         val = params.get(CLIENT_SOCKET_CLASS_FLAG);
         if(val != null)
         {
            String value = (String) val;
            if(value.length() > 0)
            {
               clientSocketClassName = value;
               log.debug("Setting ClientSocket class name to: " + clientSocketClassName);
            }
         }

         val = params.get(SocketServerInvoker.CHECK_CONNECTION_KEY);
         if(val != null)
         {
            String value = (String)val;
            if(value.length() > 0)
            {
               shouldCheckConnection = Boolean.valueOf(value).booleanValue();
            }
         }
      }
   }

   protected void finalize() throws Throwable
   {
      disconnect();
      super.finalize();
   }

   protected synchronized void handleConnect()
         throws ConnectionFailedException
   {
      initPool();
   }

   protected synchronized void handleDisconnect()
   {
      clearPools();
   }

   /**
    * 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 String getDefaultDataType()
   {
      return SerializableMarshaller.DATATYPE;
   }

   /**
    * @param sessionId
    * @param invocation
    * @param marshaller
    * @return
    * @throws java.io.IOException
    * @throws org.jboss.remoting.ConnectionFailedException
    *
    */
   protected Object transport(String sessionId, Object invocation, Map metadata,
                              Marshaller marshaller, UnMarshaller unmarshaller)
         throws IOException, ConnectionFailedException, ClassNotFoundException
   {

      Object response = null;
      long start = System.currentTimeMillis();
      SocketWrapper socketWrapper = null;
      try
      {
         socketWrapper = getConnection();
      }
      catch(Exception e)
      {
         throw new CannotConnectException("Can not get connection to server.  Problem establishing socket connection.", e);
      }
      long end = System.currentTimeMillis() - start;
      getSocketTime += end;
      try
      {
         marshaller.write(invocation, socketWrapper.getOutputStream());

         end = System.currentTimeMillis() - start;
         writeTime += end;
         start = System.currentTimeMillis();

         response = unmarshaller.read(socketWrapper.getInputStream(), null);

         end = System.currentTimeMillis() - start;
         readTime += end;
      }
      catch(Exception ex)
      {
         try
         {
            socketWrapper.close();
         }
         catch(Exception ignored)
         {
         }
         log.error("Got marshalling exception, exiting", ex);
         if(ex instanceof ClassNotFoundException)
         {
            //TODO: -TME Add better exception handling for class not found exception
            log.error("Error loading classes from remote call result.", ex);
            throw (ClassNotFoundException) ex;
         }
         else if(ex instanceof SocketTimeoutException)
         {
            throw new MarshalException("Socket timed out.  Waited " + socketWrapper.getTimeout() + " milliseconds for response while calling on " +
                                       getLocator(), ex);
         }

         throw new MarshalException("Failed to communicate.  Problem during marshalling/unmarshalling", ex);
      }

      // Put socket back in pool for reuse
      synchronized(pool)
      {
         if(pool.size() < maxPoolSize)
         {
            pool.add(socketWrapper);
         }
         else
         {
            if(log.isTraceEnabled())
            {
               log.trace("Pool was already full, than we will close the connection");
            }
            try
            {
               socketWrapper.close();
            }
            catch(Exception ignored)
            {
            }
         }
      }

      // Return response
      if(log.isTraceEnabled())
      {
         log.trace("Response: " + response);
      }

      return response;

   }

   /**
    * Close all sockets in a specific pool.
    */
   public void clearPool(ServerAddress sa)
   {
      try
      {
         LinkedList thepool = (LinkedList) connectionPools.get(sa);
         if(thepool == null)
         {
            return;
         }
         synchronized(thepool)
         {
            int size = thepool.size();
            for(int i = 0; i < size; i++)
            {
               SocketWrapper socketWrapper = (SocketWrapper) thepool.removeFirst();
               try
               {
                  socketWrapper.close();
                  socketWrapper = null;
               }
               catch(Exception ignored)
               {
               }
            }
         }
      }
      catch(Exception ex)
      {
         // ignored
      }
   }

   /**
    * Close all sockets in all pools
    */
   public void clearPools()
   {
      synchronized(connectionPools)
      {
         Iterator it = connectionPools.keySet().iterator();
         while(it.hasNext())
         {
            ServerAddress sa = (ServerAddress) it.next();
            clearPool(sa);
         }
      }
   }

   protected void initPool()
   {
      synchronized(connectionPools)
      {
         pool = (LinkedList) connectionPools.get(address);
         if(pool == null)
         {
            pool = new LinkedList();
            connectionPools.put(address, pool);
         }
      }
   }

   /**
    * Sets the number of retries to get a socket connection.
    *
    * @param numberOfRetries Must be a number greater than 0
    */
   public void setNumberOfRetries(int numberOfRetries)
   {
      if(numberOfRetries < 1)
      {
         this.numberOfRetries = MAX_RETRIES;
      }
      else
      {
         this.numberOfRetries = numberOfRetries;
      }
   }

   public int getNumberOfRetries()
   {
      return numberOfRetries;
   }

   /**
    * used for debugging (tracing) connections leaks
    */
   static int counter = 0;

   protected SocketWrapper getConnection() throws Exception
   {
      Exception failed = null;
      Socket socket = null;

      //
      // Need to retry a few times
      // on socket connection because, at least on Windoze,
      // if too many concurrent threads try to connect
      // at same time, you get ConnectionRefused
      //
      // Retrying seems to be the most performant.
      //
      // This problem always happens with RMI and seems to
      // have nothing to do with backlog or number of threads
      // waiting in accept() on the server.
      //
      for(int i = 0; i < numberOfRetries; i++)
      {
         synchronized(pool)
         {
            if(pool.size() > 0)
            {
               SocketWrapper pooled = getPooledConnection();
               if(pooled != null)
               {
                  usedPooled++;
                  return pooled;
               }
            }
         }

         try
         {
            if(log.isTraceEnabled())
            {
               log.trace("Creating socket number " + (counter++));
            }
            socket = createSocket(address.address, address.port);
            break;
         }
         catch(Exception ex)
         {
            if(i + 1 < MAX_RETRIES)
            {
               Thread.sleep(1);
               continue;
            }
            throw ex;
         }
      }
      socket.setTcpNoDelay(address.enableTcpNoDelay);
      return createClientSocket(socket, address.timeout, getLocator().getParameters());
   }

   protected SocketWrapper createClientSocket(Socket socket, int timeout, Map metadata) throws Exception
   {
      if(clientSocketConstructor == null)
      {
         ClassLoader classLoader = getClassLoader();
         if(classLoader == null)
         {
            classLoader = Thread.currentThread().getContextClassLoader();

            if(classLoader == null)
            {
               classLoader = getClass().getClassLoader();
            }
         }
         Class cl = classLoader.loadClass(clientSocketClassName);

         try
         {
            clientSocketConstructor = cl.getConstructor(new Class[]{Socket.class, Map.class, Integer.class});
         }
         catch(NoSuchMethodException e)
         {
            clientSocketConstructor = cl.getConstructor(new Class[]{Socket.class});
         }

      }

      SocketWrapper clientSocketWrapper = null;
      if(clientSocketConstructor.getParameterTypes().length == 3)
      {
         clientSocketWrapper = (SocketWrapper) clientSocketConstructor.newInstance(new Object[]{socket, metadata, new Integer(timeout)});
      }
      else
      {
         clientSocketWrapper = (SocketWrapper) clientSocketConstructor.newInstance(new Object[]{socket});
         clientSocketWrapper.setTimeout(timeout);
      }

      return clientSocketWrapper;
   }

   protected Socket createSocket(String address, int port) throws IOException
   {
      SocketFactory socketFactory = getSocketFactory();
      if (socketFactory != null)
         return socketFactory.createSocket(address, port);
      else
      return new Socket(address, port);
   }

   protected SocketWrapper getPooledConnection()
   {
      SocketWrapper socketWrapper = null;
      while(pool.size() > 0)
      {
         socketWrapper = (SocketWrapper) pool.removeFirst();
         try
         {
            if(socketWrapper != null)
            {
               if(shouldCheckConnection)
               {
               socketWrapper.checkConnection();
               return socketWrapper;
               }
               else
               {
                  if(socketWrapper.getSocket().isConnected())
                  {
                     return socketWrapper;
                  }
                  else
                  {
                     try
                     {
                        socketWrapper.close();
                     }
                     catch(IOException e)
                     {
                     }
                     return null;
                  }
               }
            }
         }
         catch(Exception ex)
         {
            if(log.isTraceEnabled())
            {
               log.trace("Couldn't reuse connection from pool");
            }
            try
            {
               socketWrapper.close();
            }
            catch(Exception ignored)
            {
            }
         }
      }
      return null;
   }


   /**
    * The name of of the server.
    */
   public String getServerHostName() throws Exception
   {
      return address.address;
   }


}
TOP

Related Classes of org.jboss.remoting.transport.socket.SocketClientInvoker

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.