1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.struts2.codebehind;
23
24 import com.opensymphony.xwork2.*;
25 import com.opensymphony.xwork2.config.Configuration;
26 import com.opensymphony.xwork2.config.ConfigurationException;
27 import com.opensymphony.xwork2.config.entities.*;
28 import com.opensymphony.xwork2.config.providers.InterceptorBuilder;
29 import com.opensymphony.xwork2.inject.Inject;
30 import com.opensymphony.xwork2.util.logging.Logger;
31 import com.opensymphony.xwork2.util.logging.LoggerFactory;
32 import org.apache.struts2.util.ClassLoaderUtils;
33
34 import javax.servlet.ServletContext;
35 import java.net.MalformedURLException;
36 import java.net.URL;
37 import java.util.Collections;
38 import java.util.LinkedHashMap;
39 import java.util.Map;
40
41 /***
42 * Uses code-behind conventions to solve the two unknown problems.
43 */
44 public class CodebehindUnknownHandler implements UnknownHandler {
45
46 protected String defaultPackageName;
47 protected ServletContext servletContext;
48 protected Map<String,ResultTypeConfig> resultsByExtension;
49 protected String templatePathPrefix;
50 protected Configuration configuration;
51 protected ObjectFactory objectFactory;
52
53 protected static final Logger LOG = LoggerFactory.getLogger(CodebehindUnknownHandler.class);
54
55 @Inject
56 public CodebehindUnknownHandler(@Inject("struts.codebehind.defaultPackage") String defaultPackage,
57 @Inject Configuration configuration) {
58
59 this.configuration = configuration;
60 this.defaultPackageName = defaultPackage;
61 resultsByExtension = new LinkedHashMap<String,ResultTypeConfig>();
62 PackageConfig parentPackage = configuration.getPackageConfig(defaultPackageName);
63 if (parentPackage == null) {
64 throw new ConfigurationException("Unknown parent package: "+parentPackage);
65 }
66 Map<String,ResultTypeConfig> results = parentPackage.getAllResultTypeConfigs();
67
68 resultsByExtension.put("jsp", results.get("dispatcher"));
69 resultsByExtension.put("vm", results.get("velocity"));
70 resultsByExtension.put("ftl", results.get("freemarker"));
71
72 }
73
74 @Inject("struts.codebehind.pathPrefix")
75 public void setPathPrefix(String prefix) {
76 this.templatePathPrefix=prefix;
77 }
78
79 @Inject
80 public void setServletContext(ServletContext servletContext) {
81 this.servletContext = servletContext;
82 }
83
84 @Inject
85 public void setObjectFactory(ObjectFactory objectFactory) {
86 this.objectFactory = objectFactory;
87 }
88
89 public ActionConfig handleUnknownAction(String namespace, String actionName)
90 throws XWorkException {
91 String pathPrefix = determinePath(templatePathPrefix, namespace);
92 ActionConfig actionConfig = null;
93 for (String ext : resultsByExtension.keySet()) {
94 if (LOG.isDebugEnabled()) {
95 LOG.debug("Trying to locate unknown action template with extension ."+ext+" in directory "+pathPrefix);
96 }
97 String path = string(pathPrefix, actionName, "." , ext);
98 try {
99 if (locateTemplate(path) != null) {
100 actionConfig = buildActionConfig(path, namespace, actionName, resultsByExtension.get(ext));
101 break;
102 }
103 } catch (MalformedURLException e) {
104 LOG.warn("Unable to parse template path: "+path+", skipping...");
105 }
106 }
107 return actionConfig;
108 }
109
110 /*** Create a new ActionConfig in the default package, with the default interceptor stack and a single result */
111 protected ActionConfig buildActionConfig(String path, String namespace, String actionName, ResultTypeConfig resultTypeConfig) {
112 final PackageConfig pkg = configuration.getPackageConfig(defaultPackageName);
113 return new ActionConfig.Builder(defaultPackageName, "execute", pkg.getDefaultClassRef())
114 .addInterceptors(InterceptorBuilder.constructInterceptorReference(new InterceptorLocator() {
115 public Object getInterceptorConfig(String name) {
116 return pkg.getAllInterceptorConfigs().get(name);
117 }
118 }, pkg.getFullDefaultInterceptorRef(),
119 Collections.EMPTY_MAP, null, objectFactory))
120 .addResultConfig(new ResultConfig.Builder(Action.SUCCESS, resultTypeConfig.getClassName())
121 .addParams(resultTypeConfig.getParams())
122 .addParam(resultTypeConfig.getDefaultResultParam(), path)
123 .build())
124 .build();
125 }
126
127 public Result handleUnknownResult(ActionContext actionContext, String actionName,
128 ActionConfig actionConfig, String resultCode) throws XWorkException {
129
130 Result result = null;
131 PackageConfig pkg = configuration.getPackageConfig(actionConfig.getPackageName());
132 String ns = pkg.getNamespace();
133 String pathPrefix = determinePath(templatePathPrefix, ns);
134
135 for (String ext : resultsByExtension.keySet()) {
136 if (LOG.isDebugEnabled()) {
137 LOG.debug("Trying to locate result with extension ."+ext+" in directory "+pathPrefix);
138 }
139 String path = string(pathPrefix, actionName, "-", resultCode, "." , ext);
140 try {
141 if (locateTemplate(path) != null) {
142 result = buildResult(path, resultCode, resultsByExtension.get(ext), actionContext);
143 break;
144 }
145 } catch (MalformedURLException e) {
146 LOG.warn("Unable to parse template path: "+path+", skipping...");
147 }
148
149 path = string(pathPrefix, actionName, "." , ext);
150 try {
151 if (locateTemplate(path) != null) {
152 result = buildResult(path, resultCode, resultsByExtension.get(ext), actionContext);
153 break;
154 }
155 } catch (MalformedURLException e) {
156 LOG.warn("Unable to parse template path: "+path+", skipping...");
157 }
158 }
159
160 return result;
161 }
162
163 protected Result buildResult(String path, String resultCode, ResultTypeConfig config, ActionContext invocationContext) {
164 ResultConfig resultConfig = new ResultConfig.Builder(resultCode, config.getClassName())
165 .addParams(config.getParams())
166 .addParam(config.getDefaultResultParam(), path)
167 .build();
168 try {
169 return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
170 } catch (Exception e) {
171 throw new XWorkException("Unable to build codebehind result", e, resultConfig);
172 }
173 }
174
175 protected String string(String... parts) {
176 StringBuilder sb = new StringBuilder();
177 for (String part : parts) {
178 sb.append(part);
179 }
180 return sb.toString();
181 }
182
183 protected String joinPaths(boolean leadingSlash, boolean trailingSlash, String... parts) {
184 StringBuilder sb = new StringBuilder();
185 if (leadingSlash) {
186 sb.append("/");
187 }
188 for (String part : parts) {
189 if (sb.length() > 0 && sb.charAt(sb.length()-1) != '/') {
190 sb.append("/");
191 }
192 sb.append(stripSlashes(part));
193 }
194 if (trailingSlash) {
195 if (sb.length() > 0 && sb.charAt(sb.length()-1) != '/') {
196 sb.append("/");
197 }
198 }
199 return sb.toString();
200 }
201
202 protected String determinePath(String prefix, String ns) {
203 return joinPaths(true, true, prefix, ns);
204 }
205
206 protected String stripLeadingSlash(String path) {
207 String result;
208 if (path != null) {
209 if (path.length() > 0) {
210 if (path.charAt(0) == '/') {
211 result = path.substring(1);
212 } else {
213 result = path;
214 }
215 } else {
216 result = path;
217 }
218 } else {
219 result = "";
220 }
221
222 return result;
223 }
224
225 protected String stripTrailingSlash(String path) {
226 String result;
227
228 if (path != null) {
229 if (path.length() > 0) {
230 if (path.charAt(path.length() - 1) == '/') {
231 result = path.substring(0, path.length()-1);
232 } else {
233 result = path;
234 }
235 } else {
236 result = path;
237 }
238 } else {
239 result = "";
240 }
241
242 return result;
243 }
244
245 protected String stripSlashes(String path) {
246 return stripLeadingSlash(stripTrailingSlash(path));
247 }
248
249 URL locateTemplate(String path) throws MalformedURLException {
250 URL template = servletContext.getResource(path);
251 if (template != null) {
252 if (LOG.isDebugEnabled()) {
253 LOG.debug("Loaded template '" + path + "' from servlet context.");
254 }
255 } else {
256 template = ClassLoaderUtils.getResource(stripLeadingSlash(path), getClass());
257 if (template != null && LOG.isDebugEnabled()) {
258 LOG.debug("Loaded template '" + stripLeadingSlash(path) + "' from class path.");
259 }
260 }
261 return template;
262 }
263
264
265 /***
266 * Not used
267 */
268 public Object handleUnknownActionMethod(Object action, String methodName) throws NoSuchMethodException {
269 throw new NoSuchMethodException();
270 }
271
272 }