001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry.resolver;
016
017 import org.apache.commons.logging.Log;
018 import org.apache.hivemind.ApplicationRuntimeException;
019 import org.apache.hivemind.ClassResolver;
020 import org.apache.hivemind.Location;
021 import org.apache.hivemind.Resource;
022 import org.apache.hivemind.impl.LocationImpl;
023 import org.apache.hivemind.util.ClasspathResource;
024 import org.apache.tapestry.INamespace;
025 import org.apache.tapestry.IRequestCycle;
026 import org.apache.tapestry.services.ClassFinder;
027 import org.apache.tapestry.spec.ComponentSpecification;
028 import org.apache.tapestry.spec.IComponentSpecification;
029
030 /**
031 * Utility class that understands the rules of component types (which may optionally have a library
032 * prefix) and can resolve the type to a {@link org.apache.tapestry.INamespace}and a
033 * {@link org.apache.tapestry.spec.IComponentSpecification}.
034 * <p>
035 * Like {@link org.apache.tapestry.resolver.PageSpecificationResolver}, if the component is not
036 * defined explicitly in the namespace, a search may occur: Performs the tricky work of resolving a
037 * page name to a page specification. The search for pages in the application namespace is the most
038 * complicated, since Tapestry searches for pages that aren't explicitly defined in the application
039 * specification. The search, based on the <i>simple-name </i> of the page, goes as follows:
040 * <ul>
041 * <li>As declared in the application specification
042 * <li><i>type</i>.jwc in the same folder as the application specification
043 * <li><i>type</i> jwc in the WEB-INF/ <i>servlet-name </i> directory of the context root
044 * <li><i>type</i>.jwc in WEB-INF
045 * <li><i>type</i>.jwc in the application root (within the context root)
046 * <li>By searching the framework namespace
047 * <li>By searching for a named class file within the org.apache.tapestry.component-class-packages
048 * property (defined within the namespace)
049 * </ul>
050 *
051 * The search for components in library namespaces is more abbreviated:
052 * <ul>
053 * <li>As declared in the library specification
054 * <li><i>type </i>.jwc in the same folder as the library specification
055 * <li>By searching the framework namespace
056 * </ul>
057 *
058 * @since 3.0
059 */
060
061 public class ComponentSpecificationResolverImpl extends AbstractSpecificationResolver implements ComponentSpecificationResolver
062 {
063 /** Set by container. */
064 private Log _log;
065
066 /** Set by resolve(). */
067 private String _type;
068
069 private ClassFinder _classFinder;
070
071 private ClassResolver _classResolver;
072
073 protected void reset()
074 {
075 _type = null;
076
077 super.reset();
078 }
079
080 /**
081 * Passed the namespace of a container (to resolve the type in) and the type to resolve,
082 * performs the processing. A "bare type" (without a library prefix) may be in the
083 * containerNamespace, or the framework namespace (a search occurs in that order).
084 *
085 * @param cycle
086 * current request cycle
087 * @param containerNamespace
088 * namespace that may contain a library referenced in the type
089 * @param type
090 * the component specification to find, either a simple name, or prefixed with a
091 * library id (defined for the container namespace)
092 * @see #getNamespace()
093 * @see #getSpecification()
094 */
095
096 public void resolve(IRequestCycle cycle, INamespace containerNamespace, String type, Location location)
097 {
098 int colonx = type.indexOf(':');
099
100 if (colonx > 0)
101 {
102 String libraryId = type.substring(0, colonx);
103 String simpleType = type.substring(colonx + 1);
104
105 resolve(cycle, containerNamespace, libraryId, simpleType, location);
106 }
107 else
108 resolve(cycle, containerNamespace, null, type, location);
109
110 IComponentSpecification spec = getSpecification();
111
112 if (spec.isDeprecated())
113 _log.warn(ResolverMessages.componentIsDeprecated(type, location));
114 }
115
116 /**
117 * Like
118 * {@link #resolve(org.apache.tapestry.IRequestCycle, org.apache.tapestry.INamespace, java.lang.String, Location)},
119 * but used when the type has already been parsed into a library id and a simple type.
120 *
121 * @param cycle
122 * current request cycle
123 * @param containerNamespace
124 * namespace that may contain a library referenced in the type
125 * @param libraryId
126 * the library id within the container namespace, or null
127 * @param type
128 * the component specification to find as a simple name (without a library prefix)
129 * @param location
130 * of reference to be resolved
131 * @throws ApplicationRuntimeException
132 * if the type cannot be resolved
133 */
134
135 public void resolve(IRequestCycle cycle, INamespace containerNamespace, String libraryId,
136 String type, Location location)
137 {
138 reset();
139 _type = type;
140
141 INamespace namespace;
142 try
143 {
144 namespace = findNamespaceForId(containerNamespace, libraryId);
145 }
146 catch (ApplicationRuntimeException e)
147 {
148 throw new ApplicationRuntimeException(e.getMessage(), location, e);
149 }
150
151 setNamespace(namespace);
152
153 if (namespace.containsComponentType(type))
154 {
155 setSpecification(namespace.getComponentSpecification(type));
156 return;
157 }
158
159 IComponentSpecification spec = searchForComponent(cycle);
160
161 // If not found after search, check to see if it's in
162 // the framework instead.
163
164 if (spec == null)
165 {
166 throw new ApplicationRuntimeException(ResolverMessages.noSuchComponentType(
167 type,
168 namespace), location, null);
169
170 }
171
172 setSpecification(spec);
173
174 // Install it into the namespace, to short-circuit any future search.
175
176 install();
177 }
178
179 // Hm. This could maybe go elsewhere, say onto ISpecificationSource
180
181 private IComponentSpecification searchForComponent(IRequestCycle cycle)
182 {
183 IComponentSpecification result = null;
184 INamespace namespace = getNamespace();
185
186 if (_log.isDebugEnabled())
187 _log.debug(ResolverMessages.resolvingComponent(_type, namespace));
188
189 String expectedName = _type + ".jwc";
190 Resource namespaceLocation = namespace.getSpecificationLocation();
191
192 // Look for appropriate file in same folder as the library (or application)
193 // specificaiton.
194
195 result = check(namespaceLocation.getRelativeResource(expectedName));
196
197 if (result != null)
198 return result;
199
200 if (namespace.isApplicationNamespace()) {
201
202 // The application namespace gets some extra searching.
203
204 result = check(getWebInfAppLocation().getRelativeResource(expectedName));
205
206 if (result == null)
207 result = check(getWebInfLocation().getRelativeResource(expectedName));
208
209 if (result == null)
210 result = check((getContextRoot().getRelativeResource(expectedName)));
211
212 if (result != null)
213 return result;
214 }
215
216 result = getDelegate().findComponentSpecification(cycle, namespace, _type);
217 if (result != null)
218 return result;
219
220 result = searchForComponentClass(namespace, _type);
221
222 if (result != null)
223 return result;
224
225 // Not in the library or app spec; does it match a component
226 // provided by the Framework?
227
228 INamespace framework = getSpecificationSource().getFrameworkNamespace();
229
230 if (framework.containsComponentType(_type))
231 return framework.getComponentSpecification(_type);
232
233 return null;
234 }
235
236 IComponentSpecification searchForComponentClass(INamespace namespace, String type)
237 {
238 String packages = namespace.getPropertyValue("org.apache.tapestry.component-class-packages");
239
240 String className = type.replace('/', '.');
241
242 Class componentClass = _classFinder.findClass(packages, className);
243 if (componentClass == null)
244 return null;
245
246 IComponentSpecification spec = new ComponentSpecification();
247
248 Resource namespaceResource = namespace.getSpecificationLocation();
249 Resource componentResource = namespaceResource.getRelativeResource(type + ".jwc");
250
251 // try classpath relative if namespace relative doesn't resolve
252
253 if (componentResource.getResourceURL() == null) {
254
255 componentResource = new ClasspathResource(_classResolver, componentClass.getName().replace('.', '/'));
256 }
257
258 Location location = new LocationImpl(componentResource);
259
260 spec.setLocation(location);
261 spec.setSpecificationLocation(componentResource);
262 spec.setComponentClassName(componentClass.getName());
263
264 return spec;
265 }
266
267 private IComponentSpecification check(Resource resource)
268 {
269 if (_log.isDebugEnabled())
270 _log.debug("Checking: " + resource);
271
272 if (resource.getResourceURL() == null)
273 return null;
274
275 return getSpecificationSource().getComponentSpecification(resource);
276 }
277
278 private void install()
279 {
280 INamespace namespace = getNamespace();
281 IComponentSpecification specification = getSpecification();
282
283 if (_log.isDebugEnabled())
284 _log.debug(ResolverMessages.installingComponent(_type, namespace, specification));
285
286 namespace.installComponentSpecification(_type, specification);
287 }
288
289 public String getType()
290 {
291 return _type;
292 }
293
294 public void setLog(Log log)
295 {
296 _log = log;
297 }
298
299 public void setClassFinder(ClassFinder classFinder)
300 {
301 _classFinder = classFinder;
302 }
303
304 public void setClassResolver(ClassResolver classResolver)
305 {
306 _classResolver = classResolver;
307 }
308 }