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 }