Save This Page
Home » openejb-3.1.2-src » org.apache » openejb » core » stateless » [javadoc | source]
    1   /**
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.openejb.core.stateless;
   18   
   19   import java.lang.reflect.Method;
   20   import java.rmi.RemoteException;
   21   import java.util.ArrayList;
   22   import java.util.HashMap;
   23   import java.util.List;
   24   import java.util.Map;
   25   import java.util.concurrent.Semaphore;
   26   
   27   import javax.ejb.SessionBean;
   28   import javax.ejb.SessionContext;
   29   import javax.naming.Context;
   30   import javax.naming.NamingException;
   31   import javax.xml.ws.WebServiceContext;
   32   
   33   import org.apache.openejb.Injection;
   34   import org.apache.openejb.OpenEJBException;
   35   import org.apache.openejb.SystemException;
   36   import org.apache.openejb.core.BaseContext;
   37   import org.apache.openejb.core.CoreDeploymentInfo;
   38   import org.apache.openejb.core.Operation;
   39   import org.apache.openejb.core.ThreadContext;
   40   import org.apache.openejb.core.interceptor.InterceptorData;
   41   import org.apache.openejb.core.interceptor.InterceptorStack;
   42   import org.apache.openejb.spi.SecurityService;
   43   import org.apache.openejb.util.Duration;
   44   import org.apache.openejb.util.LinkedListStack;
   45   import org.apache.openejb.util.LogCategory;
   46   import org.apache.openejb.util.Logger;
   47   import org.apache.openejb.util.SafeToolkit;
   48   import org.apache.openejb.util.Stack;
   49   import org.apache.xbean.recipe.ConstructionException;
   50   import org.apache.xbean.recipe.ObjectRecipe;
   51   import org.apache.xbean.recipe.Option;
   52   
   53   public class StatelessInstanceManager {
   54       private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources");
   55   
   56       protected int poolLimit = 0;
   57       protected Duration timeout;
   58       protected int beanCount = 0;
   59       protected boolean strictPooling = false;
   60   
   61       protected final SafeToolkit toolkit = SafeToolkit.getToolkit("StatefulInstanceManager");
   62       private SecurityService securityService;
   63   
   64       public StatelessInstanceManager(SecurityService securityService, Duration timeout, int poolSize, boolean strictPooling) {
   65           this.securityService = securityService;
   66           this.poolLimit = poolSize;
   67           this.strictPooling = strictPooling;
   68           this.timeout = timeout;
   69           
   70           if (strictPooling && poolSize < 1) {
   71               throw new IllegalArgumentException("Cannot use strict pooling with a pool size less than one.  Strict pooling blocks threads till an instance in the pool is available.  Please increase the pool size or set strict pooling to false");
   72           }
   73   
   74       }
   75   
   76       /**
   77        * Removes an instance from the pool and returns it for use
   78        * by the container in business methods.
   79        *
   80        * If the pool is at it's limit the StrictPooling flag will
   81        * cause this thread to wait.
   82        *
   83        * If StrictPooling is not enabled this method will create a
   84        * new stateless bean instance performing all required injection
   85        * and callbacks before returning it in a method ready state.
   86        * 
   87        * @param callContext
   88        * @return
   89        * @throws OpenEJBException
   90        */
   91       public Object getInstance(ThreadContext callContext)
   92               throws OpenEJBException {
   93           CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
   94           Data data = (Data) deploymentInfo.getContainerData();
   95           Stack pool = data.getPool();
   96           
   97           if(strictPooling){
   98           	boolean acquired;
   99               try {
  100               	if(timeout.getTime() <= 0L){
  101               		data.getSemaphore().acquire();
  102               		acquired = true;
  103               	} else {
  104                       acquired = data.getSemaphore().tryAcquire(timeout.getTime(),timeout.getUnit());
  105               	}
  106               } catch (InterruptedException e2) {
  107                   throw new OpenEJBException("Unexpected Interruption of current thread: ",e2);
  108               }
  109               if(!acquired){
  110               	throw new IllegalStateException("An invocation of the Stateless Session Bean "+deploymentInfo.getEjbName()+" has timed-out"); 
  111               }
  112           }
  113           Object bean = pool.pop();
  114   
  115           if (bean == null) {
  116   
  117               Class beanClass = deploymentInfo.getBeanClass();
  118               ObjectRecipe objectRecipe = new ObjectRecipe(beanClass);
  119               objectRecipe.allow(Option.FIELD_INJECTION);
  120               objectRecipe.allow(Option.PRIVATE_PROPERTIES);
  121               objectRecipe.allow(Option.IGNORE_MISSING_PROPERTIES);
  122               objectRecipe.allow(Option.NAMED_PARAMETERS);
  123   
  124               Operation originalOperation = callContext.getCurrentOperation();
  125               BaseContext.State[] originalAllowedStates = callContext.getCurrentAllowedStates();
  126   
  127               try {
  128                   Context ctx = deploymentInfo.getJndiEnc();                
  129                   SessionContext sessionContext;
  130                   // This needs to be synchronized as this code is multi-threaded.
  131                   // In between the lookup and the bind a bind may take place in another Thread.
  132                   // This is a fix for GERONIMO-3444
  133                   synchronized(this){
  134                       try {                    
  135                           sessionContext = (SessionContext) ctx.lookup("java:comp/EJBContext");
  136                       } catch (NamingException e1) {
  137                           sessionContext = createSessionContext();
  138                           // TODO: This should work
  139                           ctx.bind("java:comp/EJBContext", sessionContext);
  140                       }                  
  141                   }
  142                   if (javax.ejb.SessionBean.class.isAssignableFrom(beanClass) || hasSetSessionContext(beanClass)) {
  143                       callContext.setCurrentOperation(Operation.INJECTION);
  144                       callContext.setCurrentAllowedStates(StatelessContext.getStates());                    
  145                       objectRecipe.setProperty("sessionContext", sessionContext);
  146                   }     
  147                   
  148                   // This is a fix for GERONIMO-3444
  149                   synchronized(this){
  150                       try {
  151                           ctx.lookup("java:comp/WebServiceContext");
  152                       } catch (NamingException e) {
  153                           WebServiceContext wsContext = new EjbWsContext(sessionContext);
  154                           ctx.bind("java:comp/WebServiceContext", wsContext);
  155                       }
  156                   }
  157   
  158                   fillInjectionProperties(objectRecipe, beanClass, deploymentInfo, ctx);
  159   
  160                   bean = objectRecipe.create(beanClass.getClassLoader());
  161                   Map unsetProperties = objectRecipe.getUnsetProperties();
  162                   if (unsetProperties.size() > 0) {
  163                       for (Object property : unsetProperties.keySet()) {
  164                           logger.warning("Injection: No such property '" + property + "' in class " + beanClass.getName());
  165                       }
  166                   }
  167   
  168                   HashMap<String, Object> interceptorInstances = new HashMap<String, Object>();
  169                   for (InterceptorData interceptorData : deploymentInfo.getAllInterceptors()) {
  170                       if (interceptorData.getInterceptorClass().equals(beanClass)) continue;
  171   
  172                       Class clazz = interceptorData.getInterceptorClass();
  173                       ObjectRecipe interceptorRecipe = new ObjectRecipe(clazz);
  174                       interceptorRecipe.allow(Option.FIELD_INJECTION);
  175                       interceptorRecipe.allow(Option.PRIVATE_PROPERTIES);
  176                       interceptorRecipe.allow(Option.IGNORE_MISSING_PROPERTIES);
  177                       interceptorRecipe.allow(Option.NAMED_PARAMETERS);
  178   
  179                       fillInjectionProperties(interceptorRecipe, clazz, deploymentInfo, ctx);
  180   
  181                       try {
  182                           Object interceptorInstance = interceptorRecipe.create(clazz.getClassLoader());
  183                           interceptorInstances.put(clazz.getName(), interceptorInstance);
  184                       } catch (ConstructionException e) {
  185                           throw new Exception("Failed to create interceptor: " + clazz.getName(), e);
  186                       }
  187                   }
  188   
  189                   interceptorInstances.put(beanClass.getName(), bean);
  190   
  191                   callContext.setCurrentOperation(Operation.POST_CONSTRUCT);
  192                   callContext.setCurrentAllowedStates(StatelessContext.getStates());
  193                   List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
  194                   InterceptorStack interceptorStack = new InterceptorStack(bean, null, Operation.POST_CONSTRUCT, callbackInterceptors, interceptorInstances);
  195                   interceptorStack.invoke();
  196   
  197                   if (bean instanceof SessionBean){
  198                       callContext.setCurrentOperation(Operation.CREATE);
  199                       callContext.setCurrentAllowedStates(StatelessContext.getStates());
  200                       Method create = deploymentInfo.getCreateMethod();
  201                       interceptorStack = new InterceptorStack(bean, create, Operation.CREATE, new ArrayList<InterceptorData>(), new HashMap());
  202                       interceptorStack.invoke();
  203                   }
  204   
  205                   bean = new Instance(bean, interceptorInstances);
  206               } catch (Throwable e) {
  207                   if (e instanceof java.lang.reflect.InvocationTargetException) {
  208                       e = ((java.lang.reflect.InvocationTargetException) e).getTargetException();
  209                   }
  210                   String t = "The bean instance " + bean + " threw a system exception:" + e;
  211                   logger.error(t, e);
  212                   throw new org.apache.openejb.ApplicationException(new RemoteException("Cannot obtain a free instance.", e));
  213               } finally {
  214                   callContext.setCurrentOperation(originalOperation);
  215                   callContext.setCurrentAllowedStates(originalAllowedStates);
  216               }
  217           }
  218           return bean;
  219       }
  220   
  221       private static void fillInjectionProperties(ObjectRecipe objectRecipe, Class clazz, CoreDeploymentInfo deploymentInfo, Context context) {
  222           boolean usePrefix = true;
  223   
  224           try {
  225               clazz.getConstructor();
  226           } catch (NoSuchMethodException e) {
  227               // Using constructor injection
  228               // xbean can't handle the prefix yet
  229               usePrefix = false;
  230           }
  231   
  232           for (Injection injection : deploymentInfo.getInjections()) {
  233               if (!injection.getTarget().isAssignableFrom(clazz)) continue;
  234               try {
  235                   String jndiName = injection.getJndiName();
  236                   Object object = context.lookup("java:comp/env/" + jndiName);
  237                   String prefix;
  238                   if (usePrefix) {
  239                       prefix = injection.getTarget().getName() + "/";
  240                   } else {
  241                       prefix = "";
  242                   }
  243   
  244                   if (object instanceof String) {
  245                       String string = (String) object;
  246                       // Pass it in raw so it could be potentially converted to
  247                       // another data type by an xbean-reflect property editor
  248                       objectRecipe.setProperty(prefix + injection.getName(), string);
  249                   } else {
  250                       objectRecipe.setProperty(prefix + injection.getName(), object);
  251                   }
  252               } catch (NamingException e) {
  253                   logger.warning("Injection data not found in enc: jndiName='" + injection.getJndiName() + "', target=" + injection.getTarget() + "/" + injection.getName());
  254               }
  255           }
  256       }
  257   
  258       private boolean hasSetSessionContext(Class beanClass) {
  259           try {
  260               beanClass.getMethod("setSessionContext", SessionContext.class);
  261               return true;
  262           } catch (NoSuchMethodException e) {
  263               return false;
  264           }
  265       }
  266   
  267       private SessionContext createSessionContext() {
  268           return new StatelessContext(securityService);
  269       }
  270   
  271       /**
  272        * All instances are removed from the pool in getInstance(...).  They are only
  273        * returned by the StatelessContainer via this method under two circumstances.
  274        *
  275        * 1.  The business method returns normally
  276        * 2.  The business method throws an application exception
  277        *
  278        * Instances are not returned to the pool if the business method threw a system
  279        * exception.
  280        *
  281        * @param callContext
  282        * @param bean
  283        * @throws OpenEJBException
  284        */
  285       public void poolInstance(ThreadContext callContext, Object bean) throws OpenEJBException {
  286           if (bean == null) {
  287               throw new SystemException("Invalid arguments");
  288           }
  289   
  290           CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
  291           Data data = (Data) deploymentInfo.getContainerData();
  292           Stack pool = data.getPool();
  293   
  294           if (strictPooling) {
  295               pool.push(bean);
  296               data.getSemaphore().release();
  297           } else {
  298               if (pool.size() >= poolLimit) {
  299                   freeInstance(callContext, (Instance)bean);
  300               } else {
  301                   pool.push(bean);
  302               }
  303           }
  304       }
  305       
  306       /**
  307        * This method is called to release the semaphore in case of the business method 
  308        * throwing a system exception
  309        * 
  310        * @param callContext
  311        */
  312       public void discardInstance(ThreadContext callContext) {    	
  313       	if (strictPooling) {
  314       	    CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
  315               Data data = (Data) deploymentInfo.getContainerData();        
  316               data.getSemaphore().release();            
  317           }
  318       }
  319   
  320       private void freeInstance(ThreadContext callContext, Instance instance) {
  321           try {
  322               callContext.setCurrentOperation(Operation.PRE_DESTROY);
  323               callContext.setCurrentAllowedStates(StatelessContext.getStates());
  324               CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
  325   
  326               Method remove = instance.bean instanceof SessionBean? deploymentInfo.getCreateMethod(): null;
  327   
  328               List<InterceptorData> callbackInterceptors = deploymentInfo.getCallbackInterceptors();
  329               InterceptorStack interceptorStack = new InterceptorStack(instance.bean, remove, Operation.PRE_DESTROY, callbackInterceptors, instance.interceptors);
  330   
  331               interceptorStack.invoke();
  332           } catch (Throwable re) {
  333               logger.error("The bean instance " + instance + " threw a system exception:" + re, re);
  334           }
  335   
  336       }
  337   
  338       public void deploy(CoreDeploymentInfo deploymentInfo) {
  339           Data data = new Data(poolLimit, this.strictPooling);
  340           deploymentInfo.setContainerData(data);      
  341       }
  342   
  343       public void undeploy(CoreDeploymentInfo deploymentInfo) {
  344           Data data = (Data) deploymentInfo.getContainerData();
  345           if (data == null) return;
  346           Stack pool = data.getPool();
  347           //TODO ejbRemove on each bean in pool.
  348           //clean pool
  349           deploymentInfo.setContainerData(null);
  350       }
  351   
  352       private static final class Data {
  353           private final Stack pool;
  354           private Semaphore semaphore;
  355           
  356           public Data(int poolLimit, boolean strictPooling) {
  357               pool = new LinkedListStack(poolLimit);
  358               if (strictPooling) {
  359                   semaphore = new Semaphore(poolLimit);
  360               }
  361           }
  362   
  363           public Stack getPool() {
  364               return pool;
  365           }
  366           
  367           public Semaphore getSemaphore(){
  368           	return semaphore;
  369           }
  370                           
  371       }
  372   
  373   }

Save This Page
Home » openejb-3.1.2-src » org.apache » openejb » core » stateless » [javadoc | source]