001 /*
002 * Copyright 2007 Tacos.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 * under the License.
016 */
017 package net.sf.tacos.resolvers;
018
019 import org.apache.commons.logging.Log;
020 import org.apache.hivemind.ApplicationRuntimeException;
021 import org.apache.hivemind.Location;
022 import org.apache.hivemind.Resource;
023 import org.apache.hivemind.impl.DefaultClassResolver;
024 import org.apache.hivemind.impl.LocationImpl;
025 import org.apache.hivemind.util.ClasspathResource;
026 import org.apache.hivemind.util.Defense;
027 import org.apache.tapestry.INamespace;
028 import org.apache.tapestry.IRequestCycle;
029 import org.apache.tapestry.resolver.AbstractSpecificationResolver;
030 import org.apache.tapestry.resolver.ComponentSpecificationResolver;
031 import org.apache.tapestry.services.ClassFinder;
032 import org.apache.tapestry.spec.ComponentSpecification;
033 import org.apache.tapestry.spec.IComponentSpecification;
034
035 /**
036 * An AbstractSpecificationResolver similar to tapestry's. Contains additional
037 * logic that can loads tapestry templates from the classpath.
038 *
039 * @author Howard Lewis Ship
040 * @author Andreas Andreou, amplafi.com
041 */
042
043 public class ClasspathComponentSpecResolver extends
044 AbstractSpecificationResolver implements ComponentSpecificationResolver {
045
046 /** Set by container. */
047 private Log log;
048
049 /** Set by resolve(). */
050 private String type;
051
052 private ClassFinder classFinder;
053
054 protected void reset() {
055 type = null;
056 super.reset();
057 }
058
059 /**
060 * Passed the namespace of a container (to resolve the type in) and the type
061 * to resolve, performs the processing. A "bare type" (without a library
062 * prefix) may be in the containerNamespace, or the framework namespace (a
063 * search occurs in that order).
064 *
065 * @param cycle
066 * current request cycle
067 * @param containerNamespace
068 * namespace that may contain a library referenced in the type
069 * @param componentType
070 * the component specification to find, either a simple name, or
071 * prefixed with a library id (defined for the container
072 * namespace)
073 * @see #getNamespace()
074 * @see #getSpecification()
075 */
076
077 public void resolve(IRequestCycle cycle, INamespace containerNamespace,
078 String componentType, Location location) {
079 Defense.notNull(componentType, "type");
080
081 int colonx = componentType.indexOf(':');
082
083 if (colonx > 0) {
084 String libraryId = componentType.substring(0, colonx);
085 String simpleType = componentType.substring(colonx + 1);
086
087 resolve(cycle, containerNamespace, libraryId, simpleType, location);
088 } else {
089 resolve(cycle, containerNamespace, null, componentType, location);
090 }
091 }
092
093 /**
094 * Like #resolve(org.apache.tapestry.IRequestCycle,
095 * org.apache.tapestry.INamespace, java.lang.String,
096 * org.apache.tapestry.ILocation), but used when the type has already been
097 * parsed into a library id and a simple type.
098 *
099 * @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 }