View Javadoc

1   /*
2    * $Id: CodebehindUnknownHandler.java 673081 2008-07-01 13:49:01Z jeromy $
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  
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); // recurse package hiearchy
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 }