001    /*
002     * Copyright © 2008, 2009 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.RegisteredAction;
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> actionScopeCache =
043        new HashMap<Class<?>, Scope>();
044    
045      @NonNull
046      private static final Logger logger = Logger.getLogger(SsmDispatcher.class);
047        
048      @Override
049      protected Object getActionInstance(RegisteredAction action) {
050        assert action != null;
051    
052        Class<?> instanceClass = action.getActionClass();
053        Scope scope = actionScopeCache.get(instanceClass);
054        if( scope == null ) {
055          ActionScope actionScope = instanceClass.getAnnotation(ActionScope.class);
056          if( actionScope == null ) {
057            scope = Scope.STATELESS;
058          }
059          else {
060            scope = actionScope.scope();
061          }
062          actionScopeCache.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(action, 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(RegisteredAction action, Scope scope) {
076        Object result = null;
077        switch( scope ) {
078          case SESSION:
079            result = getSessionActionInstance(action);
080            break;
081          case APPLICATION:
082            result = getApplicationActionInstance(action);
083            break;
084          case STATELESS:
085            result = getStatelessActionInstance(action);
086            break;
087        }
088        assert result != null;
089        return result;
090      }
091    
092      private Object getStatelessActionInstance(RegisteredAction action) {
093        assert action != null;
094        
095        return createActionInstance(action);
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 getSessionActionInstance(RegisteredAction action) {
101        assert action != null;
102    
103        HttpSession context = WebContextManager.get().getSession();
104        Object result = WebContextManager.get().getSessionScopedObject(action.getName());
105        if( result == null ) {
106          String key = WebContext.getSessionScopedActionName(action.getName());
107          result = createActionInstance(action);
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 getApplicationActionInstance(RegisteredAction action) {
116        assert action != null;
117        
118        ServletContext context = WebContextManager.get().getServletContext();
119        Object result = WebContextManager.get().getApplicationScopedObject(action.getName());
120        if( result == null ) {
121          result = createActionInstance(action);
122          String key = WebContext.getApplicationScopedActionName(action.getName());
123          context.setAttribute(key, result);
124        }
125        return result;
126      }
127    
128    }