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.Location;
020 import org.apache.hivemind.Resource;
021 import org.apache.hivemind.impl.LocationImpl;
022 import org.apache.hivemind.util.Defense;
023 import org.apache.tapestry.INamespace;
024 import org.apache.tapestry.IRequestCycle;
025 import org.apache.tapestry.services.ClassFinder;
026 import org.apache.tapestry.spec.ComponentSpecification;
027 import org.apache.tapestry.spec.IComponentSpecification;
028
029 /**
030 * Utility class that understands the rules of component types (which may optionally have a library
031 * prefix) and can resolve the type to a {@link org.apache.tapestry.INamespace}and a
032 * {@link org.apache.tapestry.spec.IComponentSpecification}.
033 * <p>
034 * Like {@link org.apache.tapestry.resolver.PageSpecificationResolver}, if the component is not
035 * defined explicitly in the namespace, a search may occur: Performs the tricky work of resolving a
036 * page name to a page specification. The search for pages in the application namespace is the most
037 * complicated, since Tapestry searches for pages that aren't explicitly defined in the application
038 * specification. The search, based on the <i>simple-name </i> of the page, goes as follows:
039 * <ul>
040 * <li>As declared in the application specification
041 * <li><i>type</i>.jwc in the same folder as the application specification
042 * <li><i>type</i> jwc in the WEB-INF/ <i>servlet-name </i> directory of the context root
043 * <li><i>type</i>.jwc in WEB-INF
044 * <li><i>type</i>.jwc in the application root (within the context root)
045 * <li>By searching the framework namespace
046 * <li>By searching for a named class file within the org.apache.tapestry.component-class-packages
047 * property (defined within the namespace)
048 * </ul>
049 * The search for components in library namespaces is more abbreviated:
050 * <li>As declared in the library specification
051 * <li><i>type </i>.jwc in the same folder as the library specification
052 * <li>By searching the framework namespace
053 * </ul>
054 *
055 * @author Howard Lewis Ship
056 * @since 3.0
057 */
058
059 public class ComponentSpecificationResolverImpl extends AbstractSpecificationResolver implements
060 ComponentSpecificationResolver
061 {
062 /** Set by container */
063 private Log _log;
064
065 /** Set by resolve() */
066 private String _type;
067
068 private ClassFinder _classFinder;
069
070 protected void reset()
071 {
072 _type = null;
073
074 super.reset();
075 }
076
077 /**
078 * Passed the namespace of a container (to resolve the type in) and the type to resolve,
079 * performs the processing. A "bare type" (without a library prefix) may be in the
080 * containerNamespace, or the framework namespace (a search occurs in that order).
081 *
082 * @param cycle
083 * current request cycle
084 * @param containerNamespace
085 * namespace that may contain a library referenced in the type
086 * @param type
087 * the component specification to find, either a simple name, or prefixed with a
088 * library id (defined for the container namespace)
089 * @see #getNamespace()
090 * @see #getSpecification()
091 */
092
093 public void resolve(IRequestCycle cycle, INamespace containerNamespace, String type,
094 Location location)
095 {
096 Defense.notNull(type, "type");
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, org.apache.tapestry.ILocation)},
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 = findNamespaceForId(containerNamespace, libraryId);
142
143 setNamespace(namespace);
144
145 if (namespace.containsComponentType(type))
146 {
147 setSpecification(namespace.getComponentSpecification(type));
148 return;
149 }
150
151 IComponentSpecification spec = searchForComponent(cycle);
152
153 // If not found after search, check to see if it's in
154 // the framework instead.
155
156 if (spec == null)
157 {
158 throw new ApplicationRuntimeException(ResolverMessages.noSuchComponentType(
159 type,
160 namespace), location, null);
161
162 }
163
164 setSpecification(spec);
165
166 // Install it into the namespace, to short-circuit any future search.
167
168 install();
169 }
170
171 // Hm. This could maybe go elsewhere, say onto ISpecificationSource
172
173 private IComponentSpecification searchForComponent(IRequestCycle cycle)
174 {
175 IComponentSpecification result = null;
176 INamespace namespace = getNamespace();
177
178 if (_log.isDebugEnabled())
179 _log.debug(ResolverMessages.resolvingComponent(_type, namespace));
180
181 String expectedName = _type + ".jwc";
182 Resource namespaceLocation = namespace.getSpecificationLocation();
183
184 // Look for appropriate file in same folder as the library (or application)
185 // specificaiton.
186
187 result = check(namespaceLocation.getRelativeResource(expectedName));
188
189 if (result != null)
190 return result;
191
192 if (namespace.isApplicationNamespace())
193 {
194
195 // The application namespace gets some extra searching.
196
197 result = check(getWebInfAppLocation().getRelativeResource(expectedName));
198
199 if (result == null)
200 result = check(getWebInfLocation().getRelativeResource(expectedName));
201
202 if (result == null)
203 result = check((getContextRoot().getRelativeResource(expectedName)));
204
205 if (result != null)
206 return result;
207 }
208
209 result = searchForComponentClass(namespace, _type);
210
211 if (result != null)
212 return result;
213
214 // Not in the library or app spec; does it match a component
215 // provided by the Framework?
216
217 INamespace framework = getSpecificationSource().getFrameworkNamespace();
218
219 if (framework.containsComponentType(_type))
220 return framework.getComponentSpecification(_type);
221
222 return getDelegate().findComponentSpecification(cycle, namespace, _type);
223 }
224
225 IComponentSpecification searchForComponentClass(INamespace namespace, String type)
226 {
227 String packages = namespace
228 .getPropertyValue("org.apache.tapestry.component-class-packages");
229
230 String className = type.replace('/', '.');
231
232 Class componentClass = _classFinder.findClass(packages, className);
233
234 if (componentClass == null)
235 return null;
236
237 IComponentSpecification spec = new ComponentSpecification();
238
239 Resource namespaceResource = namespace.getSpecificationLocation();
240
241 Resource componentResource = namespaceResource.getRelativeResource(type + ".jwc");
242
243 Location location = new LocationImpl(componentResource);
244
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 {
254 if (_log.isDebugEnabled())
255 _log.debug("Checking: " + resource);
256
257 if (resource.getResourceURL() == null)
258 return null;
259
260 return getSpecificationSource().getComponentSpecification(resource);
261 }
262
263 private void install()
264 {
265 INamespace namespace = getNamespace();
266 IComponentSpecification specification = getSpecification();
267
268 if (_log.isDebugEnabled())
269 _log.debug(ResolverMessages.installingComponent(_type, namespace, specification));
270
271 namespace.installComponentSpecification(_type, specification);
272 }
273
274 public String getType()
275 {
276 return _type;
277 }
278
279 public void setLog(Log log)
280 {
281 _log = log;
282 }
283
284 public void setClassFinder(ClassFinder classFinder)
285 {
286 _classFinder = classFinder;
287 }
288
289 }