001    /*
002     * Copyright © 2008, 2012 Pedro Agulló Soliveres.
003     * 
004     * This file is part of DirectJNgine.
005     *
006     * DirectJNgine is free software: you can redistribute it and/or modify
007     * it under the terms of the GNU Lesser General Public License as published by
008     * the Free Software Foundation, either version 3 of the License.
009     *
010     * Commercial use is permitted to the extent that the code/component(s)
011     * do NOT become part of another Open Source or Commercially developed
012     * licensed development library or toolkit without explicit permission.
013     *
014     * DirectJNgine is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017     * GNU Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public License
020     * along with DirectJNgine.  If not, see <http://www.gnu.org/licenses/>.
021     * 
022     * This software uses the ExtJs library (http://extjs.com), which is 
023     * distributed under the GPL v3 license (see http://extjs.com/license).
024     */
025    
026    package com.softwarementors.extjs.djn.servlet.ssm;
027    
028    import java.util.HashMap;
029    import java.util.Map;
030    
031    import javax.servlet.ServletContext;
032    import javax.servlet.http.HttpSession;
033    
034    import org.apache.log4j.Logger;
035    
036    import com.softwarementors.extjs.djn.api.RegisteredMethod;
037    import com.softwarementors.extjs.djn.router.dispatcher.DispatcherBase;
038    
039    import edu.umd.cs.findbugs.annotations.NonNull;
040    
041    public class SsmDispatcher extends DispatcherBase {
042      @NonNull private static final Map<Class<?>, Scope> methodScopeCache =
043        new HashMap<Class<?>, Scope>();
044    
045      @NonNull
046      private static final Logger logger = Logger.getLogger(SsmDispatcher.class);
047        
048      @Override
049      protected Object getInvokeInstanceForNonStaticMethod(RegisteredMethod method) throws Exception {
050        assert method != null;
051    
052        Class<?> instanceClass = method.getActionClass();
053        Scope scope = methodScopeCache.get(instanceClass);
054        if( scope == null ) {
055          ActionScope methodScope = instanceClass.getAnnotation(ActionScope.class);
056          if( methodScope == null ) {
057            scope = Scope.STATELESS;
058          }
059          else {
060            scope = methodScope.scope();
061          }
062          methodScopeCache.put( instanceClass, scope);
063          if( logger.isDebugEnabled() ) {
064            logger.debug( "Action class '" + instanceClass + "' will be instantiated with " + scope.toString() + "' scope." );
065          }
066        }
067        
068        assert scope != null;
069        Object result = getAction(method, scope);
070        return result;
071      }
072    
073      @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="SF_SWITCH_NO_DEFAULT",
074          justification="Missing a 'default' branch is not a problem with enums, given the appropriate compiler settings")
075      private Object getAction(RegisteredMethod method, Scope scope) throws Exception{
076        Object result = null;
077        switch( scope ) {
078          case SESSION:
079            result = getSessionMethodInstance(method);
080            break;
081          case APPLICATION:
082            result = getApplicationMethodInstance(method);
083            break;
084          case STATELESS:
085            result = getStatelessMethodInstance(method);
086            break;
087        }
088        assert result != null;
089        return result;
090      }
091    
092      private Object getStatelessMethodInstance(RegisteredMethod method) throws Exception {
093        assert method != null;
094        
095        return createInvokeInstanceForMethodWithDefaultConstructor(method);
096      }
097      
098      /* MUST be synchronized so that two competing threads do no instantiate and store two actions "at the same time".
099       */
100      private synchronized Object getSessionMethodInstance(RegisteredMethod method) throws Exception {
101        assert method != null;
102    
103        HttpSession context = WebContextManager.get().getSession();
104        Object result = WebContextManager.get().getSessionScopedObject(method.getActionName());
105        if( result == null ) {
106          String key = WebContext.getSessionScopedActionName(method.getActionName());
107          result = createInvokeInstanceForMethodWithDefaultConstructor(method);
108          context.setAttribute(key, result);
109        }
110        return result;
111      }
112    
113      /* MUST be synchronized so that two competing threads do no instantiate and store two actions "at the same time".
114       */
115      private synchronized Object getApplicationMethodInstance(RegisteredMethod method) throws Exception {
116        assert method != null;
117        
118        ServletContext context = WebContextManager.get().getServletContext();
119        Object result = WebContextManager.get().getApplicationScopedObject(method.getActionName());
120        if( result == null ) {
121          result = createInvokeInstanceForMethodWithDefaultConstructor(method);
122          String key = WebContext.getApplicationScopedActionName(method.getActionName());
123          context.setAttribute(key, result);
124        }
125        return result;
126      }
127    
128    }