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 }