| /* |
| * ==================================================================== |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2002 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Apache" and "Apache Software Foundation" and |
| * "Apache Tapestry" must not be used to endorse or promote products |
| * derived from this software without prior written permission. For |
| * written permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * "Apache Tapestry", nor may "Apache" appear in their name, without |
| * prior written permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| package net.sf.tapestry.engine; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import net.sf.tapestry.ApplicationRuntimeException; |
| import net.sf.tapestry.IMarkupWriter; |
| import net.sf.tapestry.INamespace; |
| import net.sf.tapestry.IRenderDescription; |
| import net.sf.tapestry.IResourceResolver; |
| import net.sf.tapestry.ISpecificationSource; |
| import net.sf.tapestry.Tapestry; |
| import net.sf.tapestry.parse.SpecificationParser; |
| import net.sf.tapestry.spec.ComponentSpecification; |
| import net.sf.tapestry.spec.IApplicationSpecification; |
| import net.sf.tapestry.spec.ILibrarySpecification; |
| import net.sf.tapestry.spec.LibrarySpecification; |
| import net.sf.tapestry.util.StringSplitter; |
| import net.sf.tapestry.util.xml.DocumentParseException; |
| |
| /** |
| * Default implementation of {@link ISpecificationSource} that |
| * expects to use the normal class loader to locate component |
| * specifications from within the classpath. |
| * |
| * <p>Caches specifications in memory forever, or until {@link #reset()} is invoked. |
| * |
| * <p>An instance of this class acts like a singleton and is shared by multiple sessions, |
| * so it must be threadsafe. |
| * |
| * @author Howard Lewis Ship |
| * @version $Id$ |
| * |
| **/ |
| |
| public class DefaultSpecificationSource implements ISpecificationSource, IRenderDescription |
| { |
| private static final Log LOG = LogFactory.getLog(DefaultSpecificationSource.class); |
| |
| private IResourceResolver _resolver; |
| private IApplicationSpecification _specification; |
| |
| private SpecificationParser _parser; |
| |
| private INamespace _applicationNamespace; |
| private INamespace _frameworkNamespace; |
| |
| /** |
| * Contains previously parsed component specifications. |
| * |
| **/ |
| |
| private Map _componentCache = new HashMap(); |
| |
| /** |
| * Contains previously parsed page specifications. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| private Map _pageCache = new HashMap(); |
| |
| /** |
| * Contains previously parsed library specifications, keyed |
| * on specification resource path. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| private Map _libraryCache = new HashMap(); |
| |
| /** |
| * Contains {@link INamespace} instances, keyed on id (which will |
| * be null for the application specification). |
| * |
| **/ |
| |
| private Map _namespaceCache = new HashMap(); |
| |
| public DefaultSpecificationSource(IResourceResolver resolver, IApplicationSpecification specification) |
| { |
| _resolver = resolver; |
| _specification = specification; |
| } |
| |
| /** |
| * Clears the specification cache. This is used during debugging. |
| * |
| **/ |
| |
| public void reset() |
| { |
| _componentCache.clear(); |
| _pageCache.clear(); |
| _libraryCache.clear(); |
| _namespaceCache.clear(); |
| |
| _applicationNamespace = null; |
| _frameworkNamespace = null; |
| } |
| |
| /** |
| * Gets a specification. The type is either a component specification |
| * path, or an alias to a component (registerred in the application |
| * specification). The former always starts with a slash, the latter |
| * never does. |
| * |
| * @deprecated To be removed in 2.3, use {@link #getComponentSpecification(String)}. |
| * |
| **/ |
| |
| public ComponentSpecification getSpecification(String type) |
| { |
| return getComponentSpecification(type); |
| } |
| |
| protected ComponentSpecification parseSpecification(String resourcePath, boolean asPage) |
| { |
| ComponentSpecification result = null; |
| |
| if (LOG.isDebugEnabled()) |
| LOG.debug("Parsing component specification " + resourcePath); |
| |
| InputStream inputStream = openSpecification(resourcePath); |
| |
| SpecificationParser parser = getParser(); |
| |
| try |
| { |
| if (asPage) |
| result = _parser.parsePageSpecification(inputStream, resourcePath); |
| else |
| result = _parser.parseComponentSpecification(inputStream, resourcePath); |
| } |
| catch (DocumentParseException ex) |
| { |
| throw new ApplicationRuntimeException( |
| Tapestry.getString("DefaultSpecificationSource.unable-to-parse-specification", resourcePath), |
| ex); |
| } |
| finally |
| { |
| close(inputStream); |
| } |
| |
| result.setSpecificationResourcePath(resourcePath); |
| |
| return result; |
| } |
| |
| protected ILibrarySpecification parseLibrarySpecification(String resourcePath) |
| { |
| if (LOG.isDebugEnabled()) |
| LOG.debug("Parsing library specification " + resourcePath); |
| |
| InputStream inputStream = openSpecification(resourcePath); |
| |
| SpecificationParser parser = getParser(); |
| |
| try |
| { |
| return getParser().parseLibrarySpecification(inputStream, resourcePath, _resolver); |
| } |
| catch (DocumentParseException ex) |
| { |
| throw new ApplicationRuntimeException( |
| Tapestry.getString("DefaultSpecificationSource.unable-to-parse-specification", resourcePath), |
| ex); |
| } |
| finally |
| { |
| close(inputStream); |
| } |
| } |
| |
| /** @since 2.2 **/ |
| |
| private InputStream openSpecification(String resourcePath) |
| { |
| URL URL = _resolver.getResource(resourcePath); |
| |
| if (URL == null) |
| { |
| throw new ApplicationRuntimeException( |
| Tapestry.getString("DefaultSpecificationSource.unable-to-locate-specification", resourcePath)); |
| } |
| |
| try |
| { |
| return URL.openStream(); |
| } |
| catch (IOException ex) |
| { |
| throw new ApplicationRuntimeException( |
| Tapestry.getString("DefaultSpecificationSource.unable-to-open-specification", resourcePath), |
| ex); |
| } |
| } |
| |
| /** @since 2.2 **/ |
| |
| private void close(InputStream stream) |
| { |
| try |
| { |
| if (stream != null) |
| stream.close(); |
| } |
| catch (IOException ex) |
| { |
| // Ignore it. |
| } |
| } |
| |
| public String toString() |
| { |
| StringBuffer buffer = new StringBuffer("DefaultSpecificationSource@"); |
| buffer.append(Integer.toHexString(hashCode())); |
| |
| buffer.append('['); |
| |
| if (_componentCache != null) |
| { |
| synchronized (_componentCache) |
| { |
| buffer.append(_componentCache.keySet()); |
| } |
| } |
| |
| buffer.append(']'); |
| |
| return buffer.toString(); |
| } |
| |
| /** @since 1.0.6 **/ |
| |
| public synchronized void renderDescription(IMarkupWriter writer) |
| { |
| writer.print("DefaultSpecificationSource["); |
| |
| writeCacheDescription(writer, "page", _pageCache); |
| writer.beginEmpty("br"); |
| writer.println(); |
| |
| writeCacheDescription(writer, "component", _componentCache); |
| writer.print("]"); |
| writer.println(); |
| } |
| |
| private void writeCacheDescription(IMarkupWriter writer, String name, Map cache) |
| { |
| Set keySet = cache.keySet(); |
| |
| writer.print(Tapestry.size(keySet)); |
| writer.print(" cached "); |
| writer.print(name); |
| writer.print(" specifications:"); |
| |
| boolean first = true; |
| |
| Iterator i = keySet.iterator(); |
| while (i.hasNext()) |
| { |
| String key = (String) i.next(); |
| |
| if (first) |
| { |
| writer.begin("ul"); |
| first = false; |
| } |
| |
| writer.begin("li"); |
| writer.print(key); |
| writer.end(); |
| } |
| |
| if (!first) |
| writer.end(); // <ul> |
| } |
| |
| /** |
| * Gets a component specification. |
| * |
| * @param resourcePath the complete resource path to the specification. |
| * @throws ApplicationRuntimeException if the specification cannot be obtained. |
| * |
| **/ |
| |
| public synchronized ComponentSpecification getComponentSpecification(String resourcePath) |
| { |
| ComponentSpecification result = (ComponentSpecification) _componentCache.get(resourcePath); |
| |
| if (result == null) |
| { |
| result = parseSpecification(resourcePath, false); |
| |
| _componentCache.put(resourcePath, result); |
| } |
| |
| return result; |
| } |
| |
| public synchronized ComponentSpecification getPageSpecification(String resourcePath) |
| { |
| ComponentSpecification result = (ComponentSpecification) _pageCache.get(resourcePath); |
| |
| if (result == null) |
| { |
| result = parseSpecification(resourcePath, true); |
| |
| _pageCache.put(resourcePath, result); |
| } |
| |
| return result; |
| } |
| |
| public synchronized ILibrarySpecification getLibrarySpecification(String resourcePath) |
| { |
| ILibrarySpecification result = (LibrarySpecification) _libraryCache.get(resourcePath); |
| |
| if (result == null) |
| { |
| result = parseLibrarySpecification(resourcePath); |
| _libraryCache.put(resourcePath, result); |
| } |
| |
| return result; |
| } |
| |
| public synchronized INamespace getNamespace(String id) |
| { |
| INamespace result = (INamespace) _namespaceCache.get(id); |
| |
| if (result == null) |
| { |
| result = findNamespace(id); |
| |
| _namespaceCache.put(id, result); |
| } |
| |
| return result; |
| } |
| |
| /** @since 2.2 **/ |
| |
| private synchronized SpecificationParser getParser() |
| { |
| if (_parser == null) |
| _parser = new SpecificationParser(); |
| |
| return _parser; |
| } |
| |
| public synchronized INamespace getApplicationNamespace() |
| { |
| if (_applicationNamespace == null) |
| _applicationNamespace = new Namespace(null, null, _specification, this); |
| |
| return _applicationNamespace; |
| } |
| |
| public synchronized INamespace getFrameworkNamespace() |
| { |
| if (_frameworkNamespace == null) |
| { |
| ILibrarySpecification ls = getLibrarySpecification("/net/sf/tapestry/Framework.library"); |
| |
| _frameworkNamespace = new Namespace(INamespace.FRAMEWORK_NAMESPACE, null, ls, this); |
| } |
| |
| return _frameworkNamespace; |
| } |
| |
| /** |
| * |
| * Finds or creates the namespace. |
| * |
| * @param id the id, or id path, of the namespace. |
| * @return the namespace, |
| * @throws ApplicationRuntimeException if the namespace does not exist |
| * @since 2.2 |
| * |
| **/ |
| |
| private INamespace findNamespace(String id) |
| { |
| StringSplitter splitter = new StringSplitter('.'); |
| |
| String idPath[] = splitter.splitToArray(id); |
| |
| INamespace n = getApplicationNamespace(); |
| |
| for (int i = 0; i < idPath.length; i++) |
| { |
| n = n.getChildNamespace(idPath[i]); |
| } |
| |
| return n; |
| } |
| } |