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 062 ComponentSpecificationResolver 063 { 064 /** Set by container. */ 065 private Log _log; 066 067 /** Set by resolve(). */ 068 private String _type; 069 070 private ClassFinder _classFinder; 071 072 private ClassResolver _classResolver; 073 074 protected void reset() 075 { 076 _type = null; 077 078 super.reset(); 079 } 080 081 /** 082 * Passed the namespace of a container (to resolve the type in) and the type to resolve, 083 * performs the processing. A "bare type" (without a library prefix) may be in the 084 * containerNamespace, or the framework namespace (a search occurs in that order). 085 * 086 * @param cycle 087 * current request cycle 088 * @param containerNamespace 089 * namespace that may contain a library referenced in the type 090 * @param type 091 * the component specification to find, either a simple name, or prefixed with a 092 * library id (defined for the container namespace) 093 * @see #getNamespace() 094 * @see #getSpecification() 095 */ 096 097 public void resolve(IRequestCycle cycle, INamespace containerNamespace, String type, Location location) 098 { 099 int colonx = type.indexOf(':'); 100 101 if (colonx > 0) 102 { 103 String libraryId = type.substring(0, colonx); 104 String simpleType = type.substring(colonx + 1); 105 106 resolve(cycle, containerNamespace, libraryId, simpleType, location); 107 } 108 else 109 resolve(cycle, containerNamespace, null, type, location); 110 111 IComponentSpecification spec = getSpecification(); 112 113 if (spec.isDeprecated()) 114 _log.warn(ResolverMessages.componentIsDeprecated(type, location)); 115 } 116 117 /** 118 * Like 119 * {@link #resolve(org.apache.tapestry.IRequestCycle, org.apache.tapestry.INamespace, java.lang.String, Location)}, 120 * but used when the type has already been parsed into a library id and a simple type. 121 * 122 * @param cycle 123 * current request cycle 124 * @param containerNamespace 125 * namespace that may contain a library referenced in the type 126 * @param libraryId 127 * the library id within the container namespace, or null 128 * @param type 129 * the component specification to find as a simple name (without a library prefix) 130 * @param location 131 * of reference to be resolved 132 * @throws ApplicationRuntimeException 133 * if the type cannot be resolved 134 */ 135 136 public void resolve(IRequestCycle cycle, INamespace containerNamespace, String libraryId, 137 String type, Location location) 138 { 139 reset(); 140 _type = type; 141 142 INamespace namespace; 143 try 144 { 145 namespace = findNamespaceForId(containerNamespace, libraryId); 146 } 147 catch (ApplicationRuntimeException e) 148 { 149 throw new ApplicationRuntimeException(e.getMessage(), location, e); 150 } 151 152 setNamespace(namespace); 153 154 if (namespace.containsComponentType(type)) 155 { 156 setSpecification(namespace.getComponentSpecification(type)); 157 return; 158 } 159 160 IComponentSpecification spec = searchForComponent(cycle); 161 162 // If not found after search, check to see if it's in 163 // the framework instead. 164 165 if (spec == null) 166 { 167 throw new ApplicationRuntimeException(ResolverMessages.noSuchComponentType( 168 type, 169 namespace), location, null); 170 171 } 172 173 setSpecification(spec); 174 175 // Install it into the namespace, to short-circuit any future search. 176 177 install(); 178 } 179 180 // Hm. This could maybe go elsewhere, say onto ISpecificationSource 181 182 private IComponentSpecification searchForComponent(IRequestCycle cycle) 183 { 184 IComponentSpecification result = null; 185 INamespace namespace = getNamespace(); 186 187 if (_log.isDebugEnabled()) 188 _log.debug(ResolverMessages.resolvingComponent(_type, namespace)); 189 190 String expectedName = _type + ".jwc"; 191 Resource namespaceLocation = namespace.getSpecificationLocation(); 192 193 // Look for appropriate file in same folder as the library (or application) 194 // specificaiton. 195 196 result = check(namespaceLocation.getRelativeResource(expectedName)); 197 198 if (result != null) 199 return result; 200 201 if (namespace.isApplicationNamespace()) { 202 203 // The application namespace gets some extra searching. 204 205 result = check(getWebInfAppLocation().getRelativeResource(expectedName)); 206 207 if (result == null) 208 result = check(getWebInfLocation().getRelativeResource(expectedName)); 209 210 if (result == null) 211 result = check((getContextRoot().getRelativeResource(expectedName))); 212 213 if (result != null) 214 return result; 215 } 216 217 result = getDelegate().findComponentSpecification(cycle, namespace, _type); 218 if (result != null) 219 return result; 220 221 result = searchForComponentClass(namespace, _type); 222 223 if (result != null) 224 return result; 225 226 // Not in the library or app spec; does it match a component 227 // provided by the Framework? 228 229 INamespace framework = getSpecificationSource().getFrameworkNamespace(); 230 231 if (framework.containsComponentType(_type)) 232 return framework.getComponentSpecification(_type); 233 234 return null; 235 } 236 237 IComponentSpecification searchForComponentClass(INamespace namespace, String type) 238 { 239 String packages = namespace.getPropertyValue("org.apache.tapestry.component-class-packages"); 240 241 String className = type.replace('/', '.'); 242 243 Class componentClass = _classFinder.findClass(packages, className); 244 if (componentClass == null) 245 return null; 246 247 IComponentSpecification spec = new ComponentSpecification(); 248 249 Resource namespaceResource = namespace.getSpecificationLocation(); 250 Resource componentResource = namespaceResource.getRelativeResource(type + ".jwc"); 251 252 // try classpath relative if namespace relative doesn't resolve 253 254 if (componentResource.getResourceURL() == null) { 255 256 componentResource = new ClasspathResource(_classResolver, componentClass.getName().replace('.', '/')); 257 } 258 259 Location location = new LocationImpl(componentResource); 260 261 spec.setLocation(location); 262 spec.setSpecificationLocation(componentResource); 263 spec.setComponentClassName(componentClass.getName()); 264 265 return spec; 266 } 267 268 private IComponentSpecification check(Resource resource) 269 { 270 if (_log.isDebugEnabled()) 271 _log.debug("Checking: " + resource); 272 273 if (resource.getResourceURL() == null) 274 return null; 275 276 return getSpecificationSource().getComponentSpecification(resource); 277 } 278 279 private void install() 280 { 281 INamespace namespace = getNamespace(); 282 IComponentSpecification specification = getSpecification(); 283 284 if (_log.isDebugEnabled()) 285 _log.debug(ResolverMessages.installingComponent(_type, namespace, specification)); 286 287 namespace.installComponentSpecification(_type, specification); 288 } 289 290 public String getType() 291 { 292 return _type; 293 } 294 295 public void setLog(Log log) 296 { 297 _log = log; 298 } 299 300 public void setClassFinder(ClassFinder classFinder) 301 { 302 _classFinder = classFinder; 303 } 304 305 public void setClassResolver(ClassResolver classResolver) 306 { 307 _classResolver = classResolver; 308 } 309 }