View Javadoc

1   /*
2    *  Copyright 2007 Tacos.
3    * 
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    * 
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *  under the License.
16   */
17  package net.sf.tacos.resolvers;
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.hivemind.ApplicationRuntimeException;
21  import org.apache.hivemind.Location;
22  import org.apache.hivemind.Resource;
23  import org.apache.hivemind.impl.DefaultClassResolver;
24  import org.apache.hivemind.impl.LocationImpl;
25  import org.apache.hivemind.util.ClasspathResource;
26  import org.apache.hivemind.util.Defense;
27  import org.apache.tapestry.INamespace;
28  import org.apache.tapestry.IRequestCycle;
29  import org.apache.tapestry.resolver.AbstractSpecificationResolver;
30  import org.apache.tapestry.resolver.ComponentSpecificationResolver;
31  import org.apache.tapestry.services.ClassFinder;
32  import org.apache.tapestry.spec.ComponentSpecification;
33  import org.apache.tapestry.spec.IComponentSpecification;
34  
35  /**
36   * An AbstractSpecificationResolver similar to tapestry's. Contains additional 
37   * logic that can loads tapestry templates from the classpath.
38   * 
39   * @author Howard Lewis Ship
40   * @author Andreas Andreou, amplafi.com
41   */
42  
43  public class ClasspathComponentSpecResolver extends
44          AbstractSpecificationResolver implements ComponentSpecificationResolver {
45  
46      /** Set by container. */
47      private Log log;
48  
49      /** Set by resolve(). */
50      private String type;
51  
52      private ClassFinder classFinder;
53  
54      protected void reset() {
55          type = null;
56          super.reset();
57      }
58  
59      /**
60       * Passed the namespace of a container (to resolve the type in) and the type
61       * to resolve, performs the processing. A "bare type" (without a library
62       * prefix) may be in the containerNamespace, or the framework namespace (a
63       * search occurs in that order).
64       * 
65       * @param cycle
66       *            current request cycle
67       * @param containerNamespace
68       *            namespace that may contain a library referenced in the type
69       * @param componentType
70       *            the component specification to find, either a simple name, or
71       *            prefixed with a library id (defined for the container
72       *            namespace)
73       * @see #getNamespace()
74       * @see #getSpecification()
75       */
76  
77      public void resolve(IRequestCycle cycle, INamespace containerNamespace,
78              String componentType, Location location) {
79          Defense.notNull(componentType, "type");
80  
81          int colonx = componentType.indexOf(':');
82  
83          if (colonx > 0) {
84              String libraryId = componentType.substring(0, colonx);
85              String simpleType = componentType.substring(colonx + 1);
86  
87              resolve(cycle, containerNamespace, libraryId, simpleType, location);
88          } else {
89              resolve(cycle, containerNamespace, null, componentType, location);
90          }
91      }
92  
93      /**
94       * Like #resolve(org.apache.tapestry.IRequestCycle,
95       * org.apache.tapestry.INamespace, java.lang.String,
96       * org.apache.tapestry.ILocation), but used when the type has already been
97       * parsed into a library id and a simple type.
98       * 
99       * @param cycle
100      *            current request cycle
101      * @param containerNamespace
102      *            namespace that may contain a library referenced in the type
103      * @param libraryId
104      *            the library id within the container namespace, or null
105      * @param componentType
106      *            the component specification to find as a simple name (without
107      *            a library prefix)
108      * @param location
109      *            of reference to be resolved
110      * @throws ApplicationRuntimeException
111      *             if the type cannot be resolved
112      */
113 
114     public void resolve(IRequestCycle cycle, INamespace containerNamespace,
115             String libraryId, String componentType, Location location) {
116         reset();
117         this.type = componentType;
118 
119         INamespace namespace = findNamespaceForId(containerNamespace, libraryId);
120 
121         setNamespace(namespace);
122 
123         if (namespace.containsComponentType(type)) {
124             setSpecification(namespace.getComponentSpecification(type));
125             return;
126         }
127 
128         IComponentSpecification spec = searchForComponent(cycle);
129 
130         // If not found after search, check to see if it's in
131         // the framework instead.
132         if (spec == null) {
133             spec = doCustomSearch(cycle);
134         }
135 
136         if (spec == null) {
137             throw new ApplicationRuntimeException("no Such Component Type:" + this.type,
138                     location, null);
139         }
140 
141         setSpecification(spec);
142 
143         // Install it into the namespace, to short-circuit any future search.
144         install();
145     }
146 
147     /**
148      * Override to add custom behavior to component resolution - will be the last
149      * resolve logic to be executed.
150      * @param cycle
151      * @return
152      */
153     protected IComponentSpecification doCustomSearch(IRequestCycle cycle) {
154         return null;
155     }
156 
157     private IComponentSpecification searchForComponent(IRequestCycle cycle) {
158         INamespace namespace = getNamespace();
159 
160         // if (_log.isDebugEnabled())
161         // _log.debug(ImplMessages.resolvingComponent(_type, namespace));
162 
163         String expectedName = type + ".jwc";
164         Resource namespaceLocation = namespace.getSpecificationLocation();
165 
166         // Look for appropriate file in same folder as the library (or
167         // application) specification.
168 
169         IComponentSpecification result = check(namespaceLocation.getRelativeResource(expectedName));
170 
171         if (result != null) {
172             return result;
173         }
174         if (namespace.isApplicationNamespace()) {
175 
176             // The application namespace gets some extra searching.
177 
178             result = check(getWebInfAppLocation().getRelativeResource(
179                     expectedName));
180 
181             if (result == null) {
182                 result = check(getWebInfLocation().getRelativeResource(
183                         expectedName));
184             }
185             if (result == null) {
186                 result = check(getContextRoot()
187                         .getRelativeResource(expectedName));
188             }
189             if (result != null) {
190                 return result;
191             }
192         }
193 
194         result = searchForComponentClass(namespace, type);
195 
196         if (result != null) {
197             return result;
198         }
199 
200         // Not in the library or app spec; does it match a component
201         // provided by the Framework?
202 
203         INamespace framework = getSpecificationSource().getFrameworkNamespace();
204 
205         if (framework.containsComponentType(type)) {
206             return framework.getComponentSpecification(type);
207         }
208         return getDelegate()
209                 .findComponentSpecification(cycle, namespace, type);
210     }
211 
212     IComponentSpecification searchForComponentClass(INamespace namespace,
213             String componentType) {
214         String packages = namespace
215                 .getPropertyValue("org.apache.tapestry.component-class-packages");
216 
217         String className = componentType.replace('/', '.');
218 
219         Class componentClass = classFinder.findClass(packages, className);
220 
221         if (componentClass == null) {
222             return null;
223         }
224         Resource componentResource = null;
225         Resource namespaceResource = namespace.getSpecificationLocation();
226 
227         String fullPath = componentClass.getName().replace('.', '/');
228         ClasspathResource html = new ClasspathResource(
229                 new DefaultClassResolver(), fullPath + ".html");
230 
231         // first check near the class, if the html is there then that's where
232         // the (fictional) jwc should be - need exploded wars for this
233 
234         if (html.getResourceURL() != null && namespace.isApplicationNamespace()) {
235             componentResource = new ClasspathResource(
236                     new DefaultClassResolver(), fullPath + ".jwc");
237         } else {
238             componentResource = namespaceResource.getRelativeResource(componentType
239                     + ".jwc");
240         }
241 
242         Location location = new LocationImpl(componentResource);
243 
244         IComponentSpecification spec = new ComponentSpecification();
245         spec.setLocation(location);
246         spec.setSpecificationLocation(componentResource);
247         spec.setComponentClassName(componentClass.getName());
248 
249         return spec;
250     }
251 
252     private IComponentSpecification check(Resource resource) {
253         if (log.isDebugEnabled()) {
254             log.debug("Checking: " + resource);
255         }
256         if (resource.getResourceURL() == null) {
257             return null;
258         }
259         return getSpecificationSource().getComponentSpecification(resource);
260     }
261 
262     private void install() {
263         INamespace namespace = getNamespace();
264         IComponentSpecification specification = getSpecification();
265 
266         // if (_log.isDebugEnabled())
267         // _log.debug(ImplMessages.installingComponent(_type, namespace,
268         // specification));
269 
270         namespace.installComponentSpecification(type, specification);
271     }
272 
273     public String getType() {
274         return type;
275     }
276 
277     public void setLog(Log log) {
278         this.log = log;
279     }
280 
281     public void setClassFinder(ClassFinder classFinder) {
282         this.classFinder = classFinder;
283     }
284 
285 }