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 }