blob: 6decc980a5d1b4dcfe21d3f76c370419b189fe80 [file] [log] [blame]
// Copyright 2004, 2005 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry.resolver;
import org.apache.commons.logging.Log;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.Resource;
import org.apache.hivemind.impl.LocationImpl;
import org.apache.tapestry.INamespace;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.PageNotFoundException;
import org.apache.tapestry.Tapestry;
import org.apache.tapestry.services.ComponentPropertySource;
import org.apache.tapestry.spec.ComponentSpecification;
import org.apache.tapestry.spec.IComponentSpecification;
/**
* Performs the tricky work of resolving a page name to a page specification.
* The search for pages in the application namespace is the most complicated,
* since Tapestry searches for pages that aren't explicitly defined in the
* application specification. The search, based on the <i>simple-name </i> of
* the page, goes as follows:
* <ul>
* <li>As declared in the application specification
* <li><i>simple-name </i>.page in the same folder as the application
* specification
* <li><i>simple-name </i> page in the WEB-INF/ <i>servlet-name </i> directory
* of the context root
* <li><i>simple-name </i>.page in WEB-INF
* <li><i>simple-name </i>.page in the application root (within the context
* root)
* <li><i>simple-name </i>.html as a template in the application root, for
* which an implicit specification is generated
* <li>By searching the framework namespace
* <li>By invoking
* {@link org.apache.tapestry.resolver.ISpecificationResolverDelegate#findPageSpecification(IRequestCycle, INamespace, String)}
* </ul>
* <p>
* Pages in a component library are searched for in a more abbreviated fashion:
* <ul>
* <li>As declared in the library specification
* <li><i>simple-name </i>.page in the same folder as the library specification
* <li>By searching the framework namespace
* <li>By invoking
* {@link org.apache.tapestry.resolver.ISpecificationResolverDelegate#findPageSpecification(IRequestCycle, INamespace, String)}
* </ul>
*
* @see org.apache.tapestry.engine.IPageSource
* @author Howard Lewis Ship
* @since 3.0
*/
public class PageSpecificationResolverImpl extends
AbstractSpecificationResolver implements PageSpecificationResolver
{
private static final String WEB_INF = "/WEB-INF/";
/** set by container. */
private Log _log;
/** Set by resolve(). */
private String _simpleName;
/** @since 4.0 * */
private INamespace _applicationNamespace;
/** @since 4.0 * */
private INamespace _frameworkNamespace;
/** @since 4.0 */
private ComponentPropertySource _componentPropertySource;
public void initializeService()
{
_applicationNamespace = getSpecificationSource()
.getApplicationNamespace();
_frameworkNamespace = getSpecificationSource().getFrameworkNamespace();
super.initializeService();
}
protected void reset()
{
_simpleName = null;
super.reset();
}
/**
* Resolve the name (which may have a library id prefix) to a namespace (see
* {@link #getNamespace()}) and a specification (see
* {@link #getSpecification()}).
*
* @throws ApplicationRuntimeException
* if the name cannot be resolved
*/
public void resolve(IRequestCycle cycle, String prefixedName)
{
reset();
INamespace namespace = null;
int colonx = prefixedName.indexOf(':');
if (colonx > 0)
{
_simpleName = prefixedName.substring(colonx + 1);
String namespaceId = prefixedName.substring(0, colonx);
namespace = findNamespaceForId(_applicationNamespace, namespaceId);
}
else
{
_simpleName = prefixedName;
namespace = _applicationNamespace;
}
setNamespace(namespace);
if (namespace.containsPage(_simpleName))
{
setSpecification(namespace.getPageSpecification(_simpleName));
return;
}
// Not defined in the specification, so it's time to hunt it down.
searchForPage(cycle);
if (getSpecification() == null)
throw new PageNotFoundException(_simpleName,
ResolverMessages.noSuchPage(_simpleName, namespace));
}
public String getSimplePageName()
{
return _simpleName;
}
private void searchForPage(IRequestCycle cycle)
{
INamespace namespace = getNamespace();
if (_log.isDebugEnabled())
_log.debug(ResolverMessages.resolvingPage(_simpleName, namespace));
// Check with and without the leading slash
if (_simpleName.regionMatches(true, 0, WEB_INF, 0, WEB_INF.length())
|| _simpleName.regionMatches(true, 0, WEB_INF, 1, WEB_INF.length() - 1))
throw new ApplicationRuntimeException(ResolverMessages.webInfNotAllowed(_simpleName));
String expectedName = _simpleName + ".page";
Resource namespaceLocation = namespace.getSpecificationLocation();
// See if there's a specification file in the same folder
// as the library or application specification that's
// supposed to contain the page.
if (found(namespaceLocation, expectedName))
return;
if (namespace.isApplicationNamespace())
{
// The application namespace gets some extra searching.
if (found(getWebInfAppLocation(), expectedName))
return;
if (found(getWebInfLocation(), expectedName))
return;
if (found(getContextRoot(), expectedName))
return;
// The wierd one ... where we see if there's a template in the
// application root
// location.
String templateName = _simpleName + "." + getTemplateExtension();
Resource templateResource = getContextRoot().getRelativeResource(templateName);
if (_log.isDebugEnabled())
_log.debug(ResolverMessages.checkingResource(templateResource));
if (templateResource.getResourceURL() != null)
{
setupImplicitPage(templateResource, namespaceLocation);
return;
}
// Not found in application namespace, so maybe its a framework
// page.
if (_frameworkNamespace.containsPage(_simpleName))
{
if (_log.isDebugEnabled())
_log.debug(ResolverMessages.foundFrameworkPage(_simpleName));
setNamespace(_frameworkNamespace);
// Note: This implies that normal lookup rules don't work
// for the framework! Framework pages must be
// defined in the framework library specification.
setSpecification(_frameworkNamespace.getPageSpecification(_simpleName));
return;
}
}
// Not found by any normal rule, so its time to
// consult the delegate.
IComponentSpecification specification = getDelegate().findPageSpecification(cycle, namespace, _simpleName);
if (specification != null)
{
setSpecification(specification);
install();
}
}
private void setupImplicitPage(Resource resource, Resource namespaceLocation)
{
if (_log.isDebugEnabled())
_log.debug(ResolverMessages.foundHTMLTemplate(resource));
// TODO: The SpecFactory in Specification parser should be used in some
// way to create an IComponentSpecification!
// The virtual location of the page specification is relative to the
// namespace (typically, the application specification). This will be
// used when searching for the page's message catalog or other related assets.
Resource pageResource = namespaceLocation.getRelativeResource(_simpleName + ".page");
IComponentSpecification specification = new ComponentSpecification();
specification.setPageSpecification(true);
specification.setSpecificationLocation(pageResource);
specification.setLocation(new LocationImpl(resource));
setSpecification(specification);
install();
}
private boolean found(Resource baseResource, String expectedName)
{
Resource resource = baseResource.getRelativeResource(expectedName);
if (_log.isDebugEnabled())
_log.debug(ResolverMessages.checkingResource(resource));
if (resource.getResourceURL() == null)
return false;
setSpecification(getSpecificationSource().getPageSpecification(resource));
install();
return true;
}
private void install()
{
INamespace namespace = getNamespace();
IComponentSpecification specification = getSpecification();
if (_log.isDebugEnabled())
_log.debug(ResolverMessages.installingPage(_simpleName, namespace, specification));
namespace.installPageSpecification(_simpleName, specification);
}
/**
* If the namespace defines the template extension (as property
* {@link Tapestry#TEMPLATE_EXTENSION_PROPERTY}, then that is used,
* otherwise the default is used.
*/
private String getTemplateExtension()
{
return _componentPropertySource.getNamespaceProperty(getNamespace(), Tapestry.TEMPLATE_EXTENSION_PROPERTY);
}
/** @since 4.0 */
public void setLog(Log log)
{
_log = log;
}
/** @since 4.0 */
public void setComponentPropertySource(ComponentPropertySource componentPropertySource)
{
_componentPropertySource = componentPropertySource;
}
}