View Javadoc
1   /*
2    * $Id$
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.struts.chain.commands;
22  
23  import java.util.HashMap;
24  import java.util.Map;
25  
26  import org.apache.struts.action.Action;
27  import org.apache.struts.action.ActionMapping;
28  import org.apache.struts.chain.Constants;
29  import org.apache.struts.chain.commands.util.ClassUtils;
30  import org.apache.struts.chain.contexts.ActionContext;
31  import org.apache.struts.config.ActionConfig;
32  import org.apache.struts.config.ForwardConfig;
33  import org.apache.struts.dispatcher.Dispatcher;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  public class ExecuteDispatcher extends ActionCommandBase {
38  
39      /**
40       * The {@code Log} instance for this class.
41       */
42      private final Logger log =
43          LoggerFactory.getLogger(ExecuteDispatcher.class);
44  
45      private String defaultDispatcherType;
46  
47      /**
48       * Creates the dispatcher of the specified type.
49       *
50       * @param type the dispatcher class name
51       * @param context the current action context
52       * @return the dispatcher
53       * @throws Exception if creation fails
54       * @see ClassUtils#getApplicationInstance(String)
55       */
56      protected Dispatcher createDispatcher(String type, ActionContext context) throws Exception {
57          log.info("Initializing dispatcher of type: {}", type);
58          return (Dispatcher) ClassUtils.getApplicationInstance(type);
59      }
60  
61      @Override
62      protected boolean execute_(ActionContext context) throws Exception {
63          // Skip processing if the current request is not valid
64          Boolean valid = context.getFormValid();
65          if ((valid == null) || !valid.booleanValue()) {
66              return CONTINUE_PROCESSING;
67          }
68  
69          // Skip processing if no action is specified
70          if (context.getAction() == null) {
71              return CONTINUE_PROCESSING;
72          }
73  
74          // Skip processing if no dispatcher type specified
75          String dispatcherType = getDispatcherType(context);
76          if (dispatcherType == null) {
77              dispatcherType = defaultDispatcherType;
78              if (dispatcherType == null) {
79                  return CONTINUE_PROCESSING;
80              }
81          }
82  
83          // Obtain (or create) the dispatcher cache
84          String cacheKey = Constants.DISPATCHERS_KEY + context.getModuleConfig().getPrefix();
85          @SuppressWarnings("unchecked")
86          Map<String, Dispatcher> dispatchers = (Map<String, Dispatcher>) context.getApplicationScope().get(cacheKey);
87          if (dispatchers == null) {
88              dispatchers = new HashMap<>();
89              context.getApplicationScope().put(cacheKey, dispatchers);
90          }
91  
92          // Lookup (or create) the dispatch instance
93          Dispatcher dispatcher = null;
94          synchronized (dispatchers) {
95              ActionConfig actionConfig = context.getActionConfig();
96              String actionType = actionConfig.getType();
97              dispatcher = dispatchers.get(actionType);
98  
99              if (dispatcher == null) {
100                 dispatcher = createDispatcher(dispatcherType, context);
101                 dispatchers.put(actionType, dispatcher);
102             }
103         }
104 
105         // Dispatch
106         Object result = dispatcher.dispatch(context);
107         processDispatchResult(result, context);
108 
109         return CONTINUE_PROCESSING;
110     }
111 
112     /**
113      * Retrieves the fully-qualified dispatcher class name to instantiate when
114      * an action mapping does not specify a dispatcher.
115      *
116      * @return the default dispatcher type (fully-qualified class name)
117      * @see #getDispatcherType(ActionContext)
118      * @see #setDefaultDispatcherType(String)
119      */
120     public final String getDefaultDispatcherType() {
121         return defaultDispatcherType;
122     }
123 
124     /**
125      * Retrieves the fully-qualified class name of the dispatcher to instantiate
126      * for the specified context.
127      *
128      * @param context the current action context
129      * @return the class name or <code>null</code>
130      * @see #getDefaultDispatcherType()
131      */
132     protected String getDispatcherType(ActionContext context) {
133         String dispatcherType = null;
134         if (context != null) {
135             dispatcherType = context.getActionConfig().getDispatcher();
136         }
137         return dispatcherType;
138     }
139 
140     /**
141      * Interprets the specified dispatch result. Subclasses should override this
142      * method to provide custom result type handling. Four handlings are
143      * automatically provided:
144      * <ol>
145      * <li><code>null</code> type means the response was handled directly,
146      * and therefore no extra processing is perform</li>
147      * <li>{@link ForwardConfig} type is the classical response, and will be
148      * stored in the context</li>
149      * <li>{@link String} type represents the name of a required forward to
150      * lookup, and will be stored in the context</li>
151      * <li>{@link Void} type means the method had no return signature, and
152      * select the {@link Action#SUCCESS} action forward and store in the context</li>
153      * </ol>
154      *
155      * @param result the result value
156      * @param context the current action context
157      * @throws IllegalStateException if unknown result type or the forward
158      *         cannot be found
159      * @see ActionMapping#findRequiredForward(String)
160      */
161     protected void processDispatchResult(Object result, ActionContext context) {
162         // Null means the response was handled directly
163         if (result == null) {
164             return;
165         }
166 
167         // A forward is the classical response
168         if (result instanceof ForwardConfig) {
169             context.setForwardConfig((ForwardConfig) result);
170             return;
171         }
172 
173         // String represents the name of a forward
174         ActionConfig actionConfig = context.getActionConfig();
175         ActionMapping mapping = ((ActionMapping) actionConfig);
176         if (result instanceof String) {
177             context.setForwardConfig(mapping.findRequiredForward((String) result));
178             return;
179         }
180 
181         // Select success if no return signature
182         if (result == void.class) {
183             context.setForwardConfig(mapping.findRequiredForward(Action.SUCCESS));
184             return;
185         }
186 
187         // Unknown result type
188         throw new IllegalStateException("Unknown dispatch return type: " + result.getClass().getName());
189     }
190 
191     /**
192      * Stores the optional default dispatcher type (fully-qualified class name).
193      *
194      * @param defaultDispatcherType the dispatcher type or <code>null</code>
195      * @see #getDefaultDispatcherType()
196      */
197     public final void setDefaultDispatcherType(String defaultDispatcherType) {
198         this.defaultDispatcherType = defaultDispatcherType;
199     }
200 }