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