blob: ceaf150190ce6c2eda31f24ce7648aa50bb27146 [file] [log] [blame]
// Copyright 2004, 2008 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.engine;
import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tapestry.*;
import org.apache.tapestry.parse.SpecificationParser;
import org.apache.tapestry.resource.ClasspathResourceLocation;
import org.apache.tapestry.spec.IApplicationSpecification;
import org.apache.tapestry.spec.IComponentSpecification;
import org.apache.tapestry.spec.ILibrarySpecification;
import org.apache.tapestry.spec.LibrarySpecification;
import org.apache.tapestry.util.IRenderDescription;
import org.apache.tapestry.util.pool.Pool;
import org.apache.tapestry.util.xml.DocumentParseException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 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);
/**
* Key used to get and store {@link SpecificationParser} instances
* from the Pool.
*
* @since 3.0
*
**/
private static final String PARSER_POOL_KEY = "org.apache.tapestry.SpecificationParser";
private IResourceResolver _resolver;
private IApplicationSpecification _specification;
private INamespace _applicationNamespace;
private INamespace _frameworkNamespace;
/**
* Contains previously parsed component specifications.
*
**/
private Map _componentCache = new ConcurrentHashMap();
/**
* Contains previously parsed page specifications.
*
* @since 2.2
*
**/
private Map _pageCache = new ConcurrentHashMap();
/**
* Contains previously parsed library specifications, keyed
* on specification resource path.
*
* @since 2.2
*
**/
private Map _libraryCache = new ConcurrentHashMap();
/**
* Contains {@link INamespace} instances, keyed on id (which will
* be null for the application specification).
*
**/
private Map _namespaceCache = new ConcurrentHashMap();
/**
* Used to synchronize concurrent operations on specific resources.
*/
private ConcurrentHashMap _lockCache = new ConcurrentHashMap();
/**
* Reference to the shared {@link org.apache.tapestry.util.pool.Pool}.
*
* @see org.apache.tapestry.IEngine#getPool()
*
* @since 3.0
*
**/
private Pool _pool;
/**
* Used to synchronize global member access.
*/
private ReentrantLock _monitor = new ReentrantLock();
public DefaultSpecificationSource(
IResourceResolver resolver,
IApplicationSpecification specification,
Pool pool)
{
_resolver = resolver;
_specification = specification;
_pool = pool;
}
/**
* Clears the specification cache. This is used during debugging.
*
**/
public void reset()
{
_monitor.lock();
try
{
_componentCache.clear();
_pageCache.clear();
_libraryCache.clear();
_namespaceCache.clear();
_lockCache.clear();
_applicationNamespace = null;
_frameworkNamespace = null;
} finally
{
_monitor.unlock();
}
}
protected IComponentSpecification parseSpecification(
IResourceLocation resourceLocation,
boolean asPage)
{
IComponentSpecification result = null;
if (LOG.isDebugEnabled())
LOG.debug("Parsing component specification " + resourceLocation);
SpecificationParser parser = getParser();
try
{
if (asPage)
result = parser.parsePageSpecification(resourceLocation);
else
result = parser.parseComponentSpecification(resourceLocation);
}
catch (DocumentParseException ex)
{
throw new ApplicationRuntimeException(
Tapestry.format(
"DefaultSpecificationSource.unable-to-parse-specification",
resourceLocation),
ex);
}
finally
{
discardParser(parser);
}
return result;
}
protected ILibrarySpecification parseLibrarySpecification(IResourceLocation resourceLocation)
{
if (LOG.isDebugEnabled())
LOG.debug("Parsing library specification " + resourceLocation);
try
{
return getParser().parseLibrarySpecification(resourceLocation);
}
catch (DocumentParseException ex)
{
throw new ApplicationRuntimeException(
Tapestry.format(
"DefaultSpecificationSource.unable-to-parse-specification",
resourceLocation),
ex);
}
}
public String toString()
{
ToStringBuilder builder = new ToStringBuilder(this);
builder.append("applicationNamespace", _applicationNamespace);
builder.append("frameworkNamespace", _frameworkNamespace);
builder.append("specification", _specification);
return builder.toString();
}
/** @since 1.0.6 **/
public 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())
{
// The keys are now IResourceLocation instances
Object key = i.next();
if (first)
{
writer.begin("ul");
first = false;
}
writer.begin("li");
writer.print(key.toString());
writer.end();
}
if (!first)
writer.end(); // <ul>
}
/**
* Gets a component specification.
*
* @param resourceLocation the complete resource path to the specification.
* @throws ApplicationRuntimeException if the specification cannot be obtained.
*
**/
public IComponentSpecification getComponentSpecification(IResourceLocation resourceLocation)
{
IComponentSpecification result =
(IComponentSpecification) _componentCache.get(resourceLocation);
if (result != null)
return result;
ReentrantLock lock = getLock(resourceLocation);
lock.lock();
try
{
result = (IComponentSpecification) _componentCache.get(resourceLocation);
if (result != null)
return result;
result = parseSpecification(resourceLocation, false);
_componentCache.put(resourceLocation, result);
} finally
{
lock.unlock();
}
return result;
}
public IComponentSpecification getPageSpecification(IResourceLocation resourceLocation)
{
IComponentSpecification result = (IComponentSpecification) _pageCache.get(resourceLocation);
if (result != null)
return result;
ReentrantLock lock = getLock(resourceLocation);
lock.lock();
try
{
result = (IComponentSpecification) _pageCache.get(resourceLocation);
if (result != null)
return result;
result = parseSpecification(resourceLocation, true);
_pageCache.put(resourceLocation, result);
} finally
{
lock.unlock();
}
return result;
}
public ILibrarySpecification getLibrarySpecification(IResourceLocation resourceLocation)
{
ILibrarySpecification result = (LibrarySpecification) _libraryCache.get(resourceLocation);
if (result != null)
return result;
ReentrantLock lock = getLock(resourceLocation);
lock.lock();
try
{
result = (LibrarySpecification) _libraryCache.get(resourceLocation);
if (result != null)
return result;
result = parseLibrarySpecification(resourceLocation);
_libraryCache.put(resourceLocation, result);
} finally
{
lock.unlock();
}
return result;
}
/** @since 2.2 **/
protected SpecificationParser getParser()
{
SpecificationParser result = (SpecificationParser) _pool.retrieve(PARSER_POOL_KEY);
if (result == null)
result = new SpecificationParser(_resolver);
return result;
}
/** @since 3.0 **/
protected void discardParser(SpecificationParser parser)
{
_pool.store(PARSER_POOL_KEY, parser);
}
public INamespace getApplicationNamespace()
{
_monitor.lock();
try
{
if (_applicationNamespace == null)
_applicationNamespace = new Namespace(null, null, _specification, this);
return _applicationNamespace;
} finally
{
_monitor.unlock();
}
}
public INamespace getFrameworkNamespace()
{
_monitor.lock();
try
{
if (_frameworkNamespace == null)
{
IResourceLocation frameworkLocation =
new ClasspathResourceLocation(_resolver, "/org/apache/tapestry/Framework.library");
ILibrarySpecification ls = getLibrarySpecification(frameworkLocation);
_frameworkNamespace = new Namespace(INamespace.FRAMEWORK_NAMESPACE, null, ls, this);
}
return _frameworkNamespace;
} finally
{
_monitor.unlock();
}
}
private ReentrantLock getLock(Object key)
{
_lockCache.putIfAbsent(key, new ReentrantLock());
return (ReentrantLock) _lockCache.get(key);
}
}