Package org.jboss.invocation.pooled.server

Source Code of org.jboss.invocation.pooled.server.PooledInvoker

/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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.invocation.pooled.server;

import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.security.PrivilegedExceptionAction;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.lang.reflect.Method;
import java.rmi.NoSuchObjectException;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.net.SocketFactory;
import javax.net.ServerSocketFactory;

import org.jboss.bootstrap.spi.util.ServerConfigUtil;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.pooled.interfaces.PooledInvokerProxy;
import org.jboss.invocation.pooled.interfaces.ServerAddress;
import org.jboss.invocation.pooled.interfaces.PooledMarshalledInvocation;
import org.jboss.logging.Logger;
import org.jboss.proxy.TransactionInterceptor;
import org.jboss.system.Registry;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.tm.TransactionPropagationContextFactory;
import org.jboss.tm.TransactionPropagationContextImporter;
import org.jboss.tm.TransactionPropagationContextUtil;
import org.jboss.security.SecurityDomain;
import org.jboss.net.sockets.DefaultSocketFactory;

/**
* This invoker pools Threads and client connections to one server socket.
* The purpose is to avoid a bunch of failings of RMI.
*
* 1. Avoid making a client socket connection with every invocation call.
*    This is very expensive.  Also on windows if too many clients try
*    to connect at the same time, you get connection refused exceptions.
*    This invoker/proxy combo alleviates this.
*
* 2. Avoid creating a thread per invocation.  The client/server connection
*    is preserved and attached to the same thread.

* So we have connection pooling on the server and client side, and thread pooling
* on the server side.  Pool, is an LRU pool, so resources should be cleaned up.
*
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @author Scott.Stark@jboss.org
* @version $Revision: 81030 $
*
* @jmx:mbean extends="org.jboss.system.ServiceMBean"
*/
public class PooledInvoker extends ServiceMBeanSupport
   implements PooledInvokerMBean, Runnable
{

   /**
    * logger instance.
    */
   final static protected Logger log = Logger.getLogger(PooledInvoker.class);

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

   /**
    * The internet address to bind to by default.
    */
   protected String serverBindAddress = null;

   /**
    * The server port to bind to.
    */
   protected int serverBindPort = 0;

   /**
    * The internet address client will use to connect to the sever.
    */
   protected String clientConnectAddress = null;

   /**
    * The port a client will use to connect to the sever.
    */
   protected int clientConnectPort = 0;
   /**
    * The number of retry attempts on
    */
   protected int clientRetryCount = 1;

   protected int backlog = 200;

   /** The class name of the optional custom client socket factory */
   protected String clientSocketFactoryName;

   /** The class name of the optional custom server socket factory */
   protected String serverSocketFactoryName;

   /** An optional custom client socket factory */
   protected SocketFactory clientSocketFactory;

   /** An optional custom server socket factory */
   protected ServerSocketFactory serverSocketFactory;
   /** The server socket for */
   protected ServerSocket serverSocket = null;

   /** The name of the security domain to use with server sockets that support SSL */
   protected String sslDomain;

   protected int timeout = 60000; // 60 seconds.

   protected int maxPoolSize = 300;

   protected int clientMaxPoolSize = 300;

   protected int numAcceptThreads = 1;
   protected Thread[] acceptThreads;

   protected LRUPool clientpool;
   protected LinkedList threadpool;
   protected boolean running = true;
   /** The logging trace level flag */
   protected boolean trace = false;
   /**
    * ObjectName of the <code>transactionManagerService</code> we use.
    * Probably should not be here -- used to set txInterceptor tx mananger.
    */
   protected ObjectName transactionManagerService;

   protected PooledInvokerProxy optimizedInvokerProxy = null;
   /** A priviledged actions for MBeanServer.invoke when running with sec mgr */
   private MBeanServerAction serverAction = new MBeanServerAction();

   protected static TransactionPropagationContextFactory tpcFactory;
   protected static TransactionPropagationContextImporter tpcImporter;

   ////////////////////////////////////////////////////////////////////////
   //
   // The following methods Override the ServiceMBeanSupport base class
   //
   ////////////////////////////////////////////////////////////////////////

   protected void jmxBind()
   {
      Registry.bind(getServiceName(), optimizedInvokerProxy);
   }

   /**
    * Starts this IL, and binds it to JNDI
    *
    * @exception Exception  Description of Exception
    */
   public void startService() throws Exception
   {
      trace = log.isTraceEnabled();

      ///////////////////////////////////////////////////////////     
      // Setup the transaction stuff
      ///////////////////////////////////////////////////////////     
      InitialContext ctx = new InitialContext();

      // Get the transaction propagation context factory
      tpcFactory = TransactionPropagationContextUtil.getTPCFactory();

      // and the transaction propagation context importer
      tpcImporter = TransactionPropagationContextUtil.getTPCImporter();

      // FIXME marcf: This should not be here
      TransactionInterceptor.setTransactionManager((TransactionManager)ctx.lookup("java:/TransactionManager"));

      ///////////////////////////////////////////////////////////
      // Setup the socket level stuff
      ///////////////////////////////////////////////////////////     

      InetAddress bindAddress =
         (serverBindAddress == null || serverBindAddress.length() == 0)
            ? null
            : InetAddress.getByName(serverBindAddress);

      clientConnectAddress =
         (clientConnectAddress == null || clientConnectAddress.length() == 0)
            ? InetAddress.getLocalHost().getHostName()
            : clientConnectAddress;
      /* We need to check the address against "0.0.0.0" as this is not a valid
      address although some jdks will default to the host, while others fail
      with java.net.BindException: Cannot assign requested address: connect
      */
      clientConnectAddress = ServerConfigUtil.fixRemoteAddress(clientConnectAddress);

      // Load any custom socket factories
      loadCustomSocketFactories();

      clientpool = new LRUPool(2, maxPoolSize);
      clientpool.create();
      threadpool = new LinkedList();
       try
       {
          if( serverSocketFactory != null )
            serverSocket = serverSocketFactory.createServerSocket(serverBindPort, backlog, bindAddress);
          else
            serverSocket = new ServerSocket(serverBindPort, backlog, bindAddress);
       }
       catch( java.net.BindException be)
       {
           throw new Exception("Port "+serverBindPort+" is already in use",be);
       }
       serverBindPort = serverSocket.getLocalPort();
      clientConnectPort = (clientConnectPort == 0) ? serverSocket.getLocalPort() : clientConnectPort;

      ServerAddress sa = new ServerAddress(clientConnectAddress, clientConnectPort,
         enableTcpNoDelay, timeout, clientSocketFactory);
      optimizedInvokerProxy = new PooledInvokerProxy(sa, clientMaxPoolSize, clientRetryCount);

      ///////////////////////////////////////////////////////////     
      // Register the service with the rest of the JBoss Kernel
      ///////////////////////////////////////////////////////////     
      // Export references to the bean
      jmxBind();
      log.debug("Bound invoker for JMX node");
      ctx.close();

      acceptThreads = new Thread[numAcceptThreads];
      for (int i = 0; i < numAcceptThreads; i++)
      {
         String name = "PooledInvokerAcceptor#"+i+"-"+serverBindPort;
         acceptThreads[i] = new Thread(this, name);
         acceptThreads[i].start();
      }
   }

   public void run()
   {
      while (running)
      {
         try
         {
            Socket socket = serverSocket.accept();
            if( trace )
               log.trace("Accepted: "+socket);
            ServerThread thread = null;
            boolean newThread = false;
           
            while (thread == null)
            {
               synchronized(threadpool)
               {
                  if (threadpool.size() > 0)
                  {
                     thread = (ServerThread)threadpool.removeFirst();
                  }
               }
               if (thread == null)
               {
                  synchronized(clientpool)
                  {
                     if (clientpool.size() < maxPoolSize)
                     {
                        thread = new ServerThread(socket, this, clientpool, threadpool, timeout);
                        newThread = true;
                     }
                     if (thread == null)
                     {
                        clientpool.evict();
                        if( trace )
                           log.trace("Waiting for a thread...");
                        clientpool.wait();
                        if( trace )
                           log.trace("Notified of available thread");
                     }
                  }
               }
            }
            synchronized(clientpool)
            {
               clientpool.insert(thread, thread);
            }
           
            if (newThread)
            {
               if( trace )
                  log.trace("Created a new thread, t="+thread);
               thread.start();
            }
            else
            {
               if( trace )
                  log.trace("Reusing thread t="+thread);
               thread.wakeup(socket, timeout);
            }
         }
         catch (Throwable ex)
         {
            if (running)
               log.error("Failed to accept socket connection", ex);
         }
      }
   }

   /**
    * Stops this service, and unbinds it from JNDI.
    */
   public void stopService() throws Exception
   {
      running = false;
      maxPoolSize = 0; // so ServerThreads don't reinsert themselves
      for (int i = 0; i < acceptThreads.length; i++)
      {
         try
         {
            acceptThreads[i].interrupt();
         }
         catch (Exception ignored){}
      }
      clientpool.flush();
      for (int i = 0; i < threadpool.size(); i++)
      {
         ServerThread thread = (ServerThread)threadpool.removeFirst();
         thread.shutdown();
      }

      try
      {
         serverSocket.close();
      }
      catch(Exception e)
      {        
      }
   }

   protected void destroyService() throws Exception
   {
      // Unexport references to the bean
      Registry.unbind(getServiceName());
   }

   /**
    * The ServerProtocol will use this method to service an invocation
    * request.
    */
   public Object invoke(Invocation invocation) throws Exception
   {
      Thread currentThread = Thread.currentThread();
      ClassLoader oldCl = currentThread.getContextClassLoader();
      try
      {

         // Deserialize the transaction if it is there
         PooledMarshalledInvocation mi = (PooledMarshalledInvocation) invocation;
         invocation.setTransaction(importTPC(mi.getTransactionPropagationContext()));
         ObjectName mbean = (ObjectName) Registry.lookup(invocation.getObjectName());
         if( mbean == null )
         {
            System.err.println("NoSuchObjectException: "+invocation.getObjectName());
            throw new NoSuchObjectException("Failed to find target for objectName: "+invocation.getObjectName());
         }

         // The cl on the thread should be set in another interceptor
         Object obj = serverAction.invoke(mbean, "invoke",
               new Object[] { invocation }, Invocation.INVOKE_SIGNATURE);

         return obj;
      }
      catch (Exception e)
      {
         org.jboss.mx.util.JMXExceptionDecoder.rethrow(e);

         // the compiler does not know an exception is thrown by the above
         throw new org.jboss.util.UnreachableStatementException();
      }
      finally
      {
         currentThread.setContextClassLoader(oldCl);
      }
   }

   protected Transaction importTPC(Object tpc)
   {
      if (tpc != null)
         return tpcImporter.importTransactionPropagationContext(tpc);
      return null;
   }

   //The following are the mbean attributes for TrunkInvoker

   /**
    * Getter for property numAcceptThreads
    *
    * @return Value of property numAcceptThreads
    * @jmx:managed-attribute
    */
   public int getNumAcceptThreads()
   {
      return numAcceptThreads;
   }

   /**
    * Setter for property numAcceptThreads
    *
    * @param size New value of property numAcceptThreads.
    * @jmx:managed-attribute
    */
   public void setNumAcceptThreads(int size)
   {
      this.numAcceptThreads = size;
   }

   /**
    * Getter for property maxPoolSize;
    *
    * @return Value of property maxPoolSize.
    * @jmx:managed-attribute
    */
   public int getMaxPoolSize()
   {
      return maxPoolSize;
   }

   /**
    * Setter for property maxPoolSize.
    *
    * @param maxPoolSize New value of property maxPoolSize.
    * @jmx:managed-attribute
    */
   public void setMaxPoolSize(int maxPoolSize)
   {
      this.maxPoolSize = maxPoolSize;
   }

   /**
    * Getter for property maxPoolSize;
    *
    * @return Value of property maxPoolSize.
    * @jmx:managed-attribute
    */
   public int getClientMaxPoolSize()
   {
      return clientMaxPoolSize;
   }

   /**
    * Setter for property maxPoolSize.
    *
    * @param clientMaxPoolSize New value of property serverBindPort.
    * @jmx:managed-attribute
    */
   public void setClientMaxPoolSize(int clientMaxPoolSize)
   {
      this.clientMaxPoolSize = clientMaxPoolSize;
   }

   /**
    * Getter for property timeout
    *
    * @return Value of property timeout
    * @jmx:managed-attribute
    */
   public int getSocketTimeout()
   {
      return timeout;
   }

   /**
    * Setter for property timeout
    *
    * @param time New value of property timeout
    * @jmx:managed-attribute
    */
   public void setSocketTimeout(int time)
   {
      this.timeout = time;
   }

   /**
    *
    * @return Value of property CurrentClientPoolSize.
    * @jmx:managed-attribute
    */
   public int getCurrentClientPoolSize()
   {
      return clientpool.size();
   }

   /**
    *
    * @return Value of property CurrentThreadPoolSize.
    * @jmx:managed-attribute
    */
   public int getCurrentThreadPoolSize()
   {
      return threadpool.size();
   }

   /**
    * Getter for property serverBindPort.
    *
    * @return Value of property serverBindPort.
    * @jmx:managed-attribute
    */
   public int getServerBindPort()
   {
      return serverBindPort;
   }

   /**
    * Setter for property serverBindPort.
    *
    * @param serverBindPort New value of property serverBindPort.
    * @jmx:managed-attribute
    */
   public void setServerBindPort(int serverBindPort)
   {
      this.serverBindPort = serverBindPort;
   }

   /**
    * @jmx:managed-attribute
    */
   public String getClientConnectAddress()
   {
      return clientConnectAddress;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setClientConnectAddress(String clientConnectAddress)
   {
      this.clientConnectAddress = clientConnectAddress;
   }

   /**
    * @jmx:managed-attribute
    */
   public int getClientConnectPort()
   {
      return clientConnectPort;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setClientConnectPort(int clientConnectPort)
   {
      this.clientConnectPort = clientConnectPort;
   }

   /**
    * @jmx:managed-attribute
    */
   public int getClientRetryCount()
   {
      return clientRetryCount;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setClientRetryCount(int clientRetryCount)
   {
      this.clientRetryCount = clientRetryCount;
   }

   /**
    * @jmx:managed-attribute
    */
   public int getBacklog()
   {
      return backlog;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setBacklog(int backlog)
   {
      this.backlog = backlog;
   }

   /**
    * @jmx:managed-attribute
    */
   public boolean isEnableTcpNoDelay()
   {
      return enableTcpNoDelay;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setEnableTcpNoDelay(boolean enableTcpNoDelay)
   {
      this.enableTcpNoDelay = enableTcpNoDelay;
   }

   /**
    * @jmx:managed-attribute
    */
   public String getServerBindAddress()
   {
      return serverBindAddress;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setServerBindAddress(String serverBindAddress)
   {
      this.serverBindAddress = serverBindAddress;
   }

   /**
    * @jmx:managed-attribute
    */
   public String getClientSocketFactoryName()
   {
      return clientSocketFactoryName;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setClientSocketFactoryName(String clientSocketFactoryName)
   {
      this.clientSocketFactoryName = clientSocketFactoryName;
   }

   /**
    * @jmx:managed-attribute
    */
   public String getServerSocketFactoryName()
   {
      return serverSocketFactoryName;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setServerSocketFactoryName(String serverSocketFactoryName)
   {
      this.serverSocketFactoryName = serverSocketFactoryName;
   }

   /**
    * @jmx:managed-attribute
    */
   public SocketFactory getClientSocketFactory()
   {
      return clientSocketFactory;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setClientSocketFactory(SocketFactory clientSocketFactory)
   {
      this.clientSocketFactory = clientSocketFactory;
   }

   /**
    * @jmx:managed-attribute
    */
   public ServerSocket getServerSocket()
   {
      return serverSocket;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setServerSocket(ServerSocket serverSocket)
   {
      this.serverSocket = serverSocket;
   }

   /**
    * @jmx:managed-attribute
    */
   public String getSslDomain()
   {
      return sslDomain;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setSslDomain(String sslDomain)
   {
      this.sslDomain = sslDomain;
   }

   /**
    * @jmx:managed-attribute
    */
   public ServerSocketFactory getServerSocketFactory()
   {
      return serverSocketFactory;
   }

   /**
    * @jmx:managed-attribute
    */
   public void setServerSocketFactory(ServerSocketFactory serverSocketFactory)
   {
      this.serverSocketFactory = serverSocketFactory;
   }
  
   /**
    * mbean get-set pair for field transactionManagerService
    * Get the value of transactionManagerService
    * @return value of transactionManagerService
    *
    * @jmx:managed-attribute
    */
   public ObjectName getTransactionManagerService()
   {
      return transactionManagerService;
   }
  
  
   /**
    * Set the value of transactionManagerService
    * @param transactionManagerService  Value to assign to transactionManagerService
    *
    * @jmx:managed-attribute
    */
   public void setTransactionManagerService(ObjectName transactionManagerService)
   {
      this.transactionManagerService = transactionManagerService;
   }
  
   /**
    * @jmx:managed-attribute
    */
   public PooledInvokerProxy getOptimizedInvokerProxy()
   {
      return optimizedInvokerProxy;
   }

   /** Load and instantiate the clientSocketFactory, serverSocketFactory using
    the TCL and set the bind address and SSL domain if the serverSocketFactory
    supports it.
   */
   protected void loadCustomSocketFactories()
   {
      ClassLoader loader = Thread.currentThread().getContextClassLoader();

      try
      {
         if( clientSocketFactoryName != null )
         {
            Class csfClass = loader.loadClass(clientSocketFactoryName);
            clientSocketFactory = (SocketFactory) csfClass.newInstance();
         }
      }
      catch (Exception e)
      {
         log.error("Failed to load client socket factory", e);
         clientSocketFactory = null;
      }

      try
      {
         if( serverSocketFactory == null )
         {
            if( serverSocketFactoryName != null )
            {
               Class ssfClass = loader.loadClass(serverSocketFactoryName);
               serverSocketFactory = (ServerSocketFactory) ssfClass.newInstance();
               if( serverBindAddress != null )
               {
                  // See if the server socket supports setBindAddress(String)
                  try
                  {
                     Class[] parameterTypes = {String.class};
                     Method m = ssfClass.getMethod("setBindAddress", parameterTypes);
                     Object[] args = {serverBindAddress};
                     m.invoke(serverSocketFactory, args);
                  }
                  catch (NoSuchMethodException e)
                  {
                     log.warn("Socket factory does not support setBindAddress(String)");
                     // Go with default address
                  }
                  catch (Exception e)
                  {
                     log.warn("Failed to setBindAddress="+serverBindAddress+" on socket factory", e);
                     // Go with default address
                  }
               }
               /* See if the server socket supports setSecurityDomain(SecurityDomain)
               if an sslDomain was specified
               */
               if( sslDomain != null )
               {
                  try
                  {
                     InitialContext ctx = new InitialContext();
                     SecurityDomain domain = (SecurityDomain) ctx.lookup(sslDomain);
                     Class[] parameterTypes = {SecurityDomain.class};
                     Method m = ssfClass.getMethod("setSecurityDomain", parameterTypes);
                     Object[] args = {domain};
                     m.invoke(serverSocketFactory, args);
                  }
                  catch(NoSuchMethodException e)
                  {
                     log.error("Socket factory does not support setSecurityDomain(SecurityDomain)");
                  }
                  catch(Exception e)
                  {
                     log.error("Failed to setSecurityDomain="+sslDomain+" on socket factory", e);
                  }
               }
            }
            // If a bind address was specified create a DefaultSocketFactory
            else if( serverBindAddress != null )
            {
               DefaultSocketFactory defaultFactory = new DefaultSocketFactory(backlog);
               serverSocketFactory = defaultFactory;
               try
               {
                  defaultFactory.setBindAddress(serverBindAddress);
               }
               catch (UnknownHostException e)
               {
                  log.error("Failed to setBindAddress="+serverBindAddress+" on socket factory", e);
               }
            }
         }
      }
      catch (Exception e)
      {
         log.error("operation failed", e);
         serverSocketFactory = null;
      }
   }

   /** Perform the MBeanServer.invoke op in a PrivilegedExceptionAction if
    * running with a security manager.
    */
   class MBeanServerAction implements PrivilegedExceptionAction
   {
      private ObjectName target;
      String method;
      Object[] args;
      String[] sig;

      MBeanServerAction()
      { 
      }
      MBeanServerAction(ObjectName target, String method, Object[] args, String[] sig)
      {
         this.target = target;
         this.method = method;
         this.args = args;
         this.sig = sig;
      }

      public Object run() throws Exception
      {
         Object rtnValue = server.invoke(target, method, args, sig);
         return rtnValue;
      }
      Object invoke(ObjectName target, String method, Object[] args, String[] sig)
         throws Exception
      {
         SecurityManager sm = System.getSecurityManager();
         Object rtnValue = null;
         if( sm == null )
         {
            // Direct invocation on MBeanServer
            rtnValue = server.invoke(target, method, args, sig);
         }
         else
         {
            try
            {
               // Encapsulate the invocation in a PrivilegedExceptionAction
               MBeanServerAction action = new MBeanServerAction(target, method, args, sig);
               rtnValue = AccessController.doPrivileged(action);
            }
            catch (PrivilegedActionException e)
            {
               Exception ex = e.getException();
               throw ex;
            }
         }
         return rtnValue;
      }
   }
}
// vim:expandtab:tabstop=3:shiftwidth=3
TOP

Related Classes of org.jboss.invocation.pooled.server.PooledInvoker

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.