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.Resource; 020 import org.apache.hivemind.impl.LocationImpl; 021 import org.apache.tapestry.INamespace; 022 import org.apache.tapestry.IRequestCycle; 023 import org.apache.tapestry.PageNotFoundException; 024 import org.apache.tapestry.Tapestry; 025 import org.apache.tapestry.services.ComponentPropertySource; 026 import org.apache.tapestry.spec.ComponentSpecification; 027 import org.apache.tapestry.spec.IComponentSpecification; 028 029 /** 030 * Performs the tricky work of resolving a page name to a page specification. 031 * The search for pages in the application namespace is the most complicated, 032 * since Tapestry searches for pages that aren't explicitly defined in the 033 * application specification. The search, based on the <i>simple-name </i> of 034 * the page, goes as follows: 035 * <ul> 036 * <li>As declared in the application specification 037 * <li><i>simple-name </i>.page in the same folder as the application 038 * specification 039 * <li><i>simple-name </i> page in the WEB-INF/ <i>servlet-name </i> directory 040 * of the context root 041 * <li><i>simple-name </i>.page in WEB-INF 042 * <li><i>simple-name </i>.page in the application root (within the context 043 * root) 044 * <li><i>simple-name </i>.html as a template in the application root, for 045 * which an implicit specification is generated 046 * <li>By searching the framework namespace 047 * <li>By invoking 048 * {@link org.apache.tapestry.resolver.ISpecificationResolverDelegate#findPageSpecification(IRequestCycle, INamespace, String)} 049 * </ul> 050 * <p> 051 * Pages in a component library are searched for in a more abbreviated fashion: 052 * <ul> 053 * <li>As declared in the library specification 054 * <li><i>simple-name </i>.page in the same folder as the library specification 055 * <li>By searching the framework namespace 056 * <li>By invoking 057 * {@link org.apache.tapestry.resolver.ISpecificationResolverDelegate#findPageSpecification(IRequestCycle, INamespace, String)} 058 * </ul> 059 * 060 * @see org.apache.tapestry.engine.IPageSource 061 * @author Howard Lewis Ship 062 * @since 3.0 063 */ 064 065 public class PageSpecificationResolverImpl extends 066 AbstractSpecificationResolver implements PageSpecificationResolver 067 { 068 069 private static final String WEB_INF = "/WEB-INF/"; 070 071 /** set by container. */ 072 private Log _log; 073 074 /** Set by resolve(). */ 075 private String _simpleName; 076 077 /** @since 4.0 * */ 078 private INamespace _applicationNamespace; 079 080 /** @since 4.0 * */ 081 private INamespace _frameworkNamespace; 082 083 /** @since 4.0 */ 084 085 private ComponentPropertySource _componentPropertySource; 086 087 public void initializeService() 088 { 089 _applicationNamespace = getSpecificationSource() 090 .getApplicationNamespace(); 091 _frameworkNamespace = getSpecificationSource().getFrameworkNamespace(); 092 093 super.initializeService(); 094 } 095 096 protected void reset() 097 { 098 _simpleName = null; 099 100 super.reset(); 101 } 102 103 /** 104 * Resolve the name (which may have a library id prefix) to a namespace (see 105 * {@link #getNamespace()}) and a specification (see 106 * {@link #getSpecification()}). 107 * 108 * @throws ApplicationRuntimeException 109 * if the name cannot be resolved 110 */ 111 112 public void resolve(IRequestCycle cycle, String prefixedName) 113 { 114 reset(); 115 116 INamespace namespace = null; 117 118 int colonx = prefixedName.indexOf(':'); 119 120 if (colonx > 0) 121 { 122 _simpleName = prefixedName.substring(colonx + 1); 123 String namespaceId = prefixedName.substring(0, colonx); 124 125 namespace = findNamespaceForId(_applicationNamespace, namespaceId); 126 } 127 else 128 { 129 _simpleName = prefixedName; 130 131 namespace = _applicationNamespace; 132 } 133 134 setNamespace(namespace); 135 136 if (namespace.containsPage(_simpleName)) 137 { 138 setSpecification(namespace.getPageSpecification(_simpleName)); 139 return; 140 } 141 142 // Not defined in the specification, so it's time to hunt it down. 143 144 searchForPage(cycle); 145 146 if (getSpecification() == null) 147 throw new PageNotFoundException(_simpleName, 148 ResolverMessages.noSuchPage(_simpleName, namespace)); 149 } 150 151 public String getSimplePageName() 152 { 153 return _simpleName; 154 } 155 156 private void searchForPage(IRequestCycle cycle) 157 { 158 INamespace namespace = getNamespace(); 159 160 if (_log.isDebugEnabled()) 161 _log.debug(ResolverMessages.resolvingPage(_simpleName, namespace)); 162 163 // Check with and without the leading slash 164 165 if (_simpleName.regionMatches(true, 0, WEB_INF, 0, WEB_INF.length()) 166 || _simpleName.regionMatches(true, 0, WEB_INF, 1, WEB_INF.length() - 1)) 167 throw new ApplicationRuntimeException(ResolverMessages.webInfNotAllowed(_simpleName)); 168 169 String expectedName = _simpleName + ".page"; 170 171 Resource namespaceLocation = namespace.getSpecificationLocation(); 172 173 // See if there's a specification file in the same folder 174 // as the library or application specification that's 175 // supposed to contain the page. 176 177 if (found(namespaceLocation, expectedName)) 178 return; 179 180 if (namespace.isApplicationNamespace()) 181 { 182 183 // The application namespace gets some extra searching. 184 185 if (found(getWebInfAppLocation(), expectedName)) 186 return; 187 188 if (found(getWebInfLocation(), expectedName)) 189 return; 190 191 if (found(getContextRoot(), expectedName)) 192 return; 193 194 // The wierd one ... where we see if there's a template in the 195 // application root 196 // location. 197 198 String templateName = _simpleName + "." + getTemplateExtension(); 199 200 Resource templateResource = getContextRoot().getRelativeResource(templateName); 201 202 if (_log.isDebugEnabled()) 203 _log.debug(ResolverMessages.checkingResource(templateResource)); 204 205 if (templateResource.getResourceURL() != null) 206 { 207 setupImplicitPage(templateResource, namespaceLocation); 208 return; 209 } 210 211 // Not found in application namespace, so maybe its a framework 212 // page. 213 214 if (_frameworkNamespace.containsPage(_simpleName)) 215 { 216 if (_log.isDebugEnabled()) 217 _log.debug(ResolverMessages.foundFrameworkPage(_simpleName)); 218 219 setNamespace(_frameworkNamespace); 220 221 // Note: This implies that normal lookup rules don't work 222 // for the framework! Framework pages must be 223 // defined in the framework library specification. 224 225 setSpecification(_frameworkNamespace.getPageSpecification(_simpleName)); 226 return; 227 } 228 } 229 230 // Not found by any normal rule, so its time to 231 // consult the delegate. 232 233 IComponentSpecification specification = getDelegate().findPageSpecification(cycle, namespace, _simpleName); 234 235 if (specification != null) 236 { 237 setSpecification(specification); 238 install(); 239 } 240 } 241 242 private void setupImplicitPage(Resource resource, Resource namespaceLocation) 243 { 244 if (_log.isDebugEnabled()) 245 _log.debug(ResolverMessages.foundHTMLTemplate(resource)); 246 247 // TODO: The SpecFactory in Specification parser should be used in some 248 // way to create an IComponentSpecification! 249 250 // The virtual location of the page specification is relative to the 251 // namespace (typically, the application specification). This will be 252 // used when searching for the page's message catalog or other related assets. 253 254 Resource pageResource = namespaceLocation.getRelativeResource(_simpleName + ".page"); 255 256 IComponentSpecification specification = new ComponentSpecification(); 257 specification.setPageSpecification(true); 258 specification.setSpecificationLocation(pageResource); 259 specification.setLocation(new LocationImpl(resource)); 260 261 setSpecification(specification); 262 263 install(); 264 } 265 266 private boolean found(Resource baseResource, String expectedName) 267 { 268 Resource resource = baseResource.getRelativeResource(expectedName); 269 270 if (_log.isDebugEnabled()) 271 _log.debug(ResolverMessages.checkingResource(resource)); 272 273 if (resource.getResourceURL() == null) 274 return false; 275 276 setSpecification(getSpecificationSource().getPageSpecification(resource)); 277 278 install(); 279 280 return true; 281 } 282 283 private void install() 284 { 285 INamespace namespace = getNamespace(); 286 IComponentSpecification specification = getSpecification(); 287 288 if (_log.isDebugEnabled()) 289 _log.debug(ResolverMessages.installingPage(_simpleName, namespace, specification)); 290 291 namespace.installPageSpecification(_simpleName, specification); 292 } 293 294 /** 295 * If the namespace defines the template extension (as property 296 * {@link Tapestry#TEMPLATE_EXTENSION_PROPERTY}, then that is used, 297 * otherwise the default is used. 298 */ 299 300 private String getTemplateExtension() 301 { 302 return _componentPropertySource.getNamespaceProperty(getNamespace(), Tapestry.TEMPLATE_EXTENSION_PROPERTY); 303 } 304 305 /** @since 4.0 */ 306 307 public void setLog(Log log) 308 { 309 _log = log; 310 } 311 312 /** @since 4.0 */ 313 public void setComponentPropertySource(ComponentPropertySource componentPropertySource) 314 { 315 _componentPropertySource = componentPropertySource; 316 } 317 }