blob: 40a00c01b9d21513b6908c47247b374a21be4cc8 [file] [log] [blame]
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.tiles.impl;
import java.io.IOException;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import org.apache.tiles.Attribute;
import org.apache.tiles.AttributeContext;
import org.apache.tiles.BasicAttributeContext;
import org.apache.tiles.Definition;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.definition.DefinitionsFactory;
import org.apache.tiles.definition.NoSuchDefinitionException;
import org.apache.tiles.evaluator.AttributeEvaluator;
import org.apache.tiles.evaluator.AttributeEvaluatorFactory;
import org.apache.tiles.evaluator.AttributeEvaluatorFactoryAware;
import org.apache.tiles.preparer.ViewPreparer;
import org.apache.tiles.preparer.factory.NoSuchPreparerException;
import org.apache.tiles.preparer.factory.PreparerFactory;
import org.apache.tiles.request.ApplicationContext;
import org.apache.tiles.request.Request;
import org.apache.tiles.request.render.CannotRenderException;
import org.apache.tiles.request.render.Renderer;
import org.apache.tiles.request.render.RendererFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Basic implementation of the tiles container interface.
* In most cases, this container will be customized by
* injecting customized services, not necessarily by
* override the container
*
* @since 2.0
* @version $Rev$ $Date$
*/
public class BasicTilesContainer implements TilesContainer,
AttributeEvaluatorFactoryAware {
/**
* Name used to store attribute context stack.
*/
private static final String ATTRIBUTE_CONTEXT_STACK =
"org.apache.tiles.AttributeContext.STACK";
/**
* Log instance for all BasicTilesContainer
* instances.
*/
private final Logger log = LoggerFactory
.getLogger(BasicTilesContainer.class);
/**
* The Tiles application context object.
*/
private ApplicationContext context;
/**
* The definitions factory.
*/
private DefinitionsFactory definitionsFactory;
/**
* The preparer factory.
*/
private PreparerFactory preparerFactory;
/**
* The renderer factory.
*/
private RendererFactory rendererFactory;
/**
* The attribute evaluator.
*/
private AttributeEvaluatorFactory attributeEvaluatorFactory;
/** {@inheritDoc} */
public AttributeContext startContext(Request request) {
AttributeContext context = new BasicAttributeContext();
Deque<AttributeContext> stack = getContextStack(request);
if (!stack.isEmpty()) {
AttributeContext parent = stack.peek();
context.inheritCascadedAttributes(parent);
}
stack.push(context);
return context;
}
/** {@inheritDoc} */
public void endContext(Request request) {
popContext(request);
}
/** {@inheritDoc} */
public void renderContext(Request request) {
AttributeContext attributeContext = getAttributeContext(request);
render(request, attributeContext);
}
/**
* Returns the Tiles application context used by this container.
*
* @return the application context for this container.
*/
public ApplicationContext getApplicationContext() {
return context;
}
/**
* Sets the Tiles application context to use.
*
* @param context The Tiles application context.
*/
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
/** {@inheritDoc} */
public AttributeContext getAttributeContext(Request request) {
AttributeContext context = getContext(request);
if (context == null) {
context = new BasicAttributeContext();
pushContext(context, request);
}
return context;
}
/**
* Returns the definitions factory.
*
* @return The definitions factory used by this container.
*/
public DefinitionsFactory getDefinitionsFactory() {
return definitionsFactory;
}
/**
* Set the definitions factory. This method first ensures
* that the container has not yet been initialized.
*
* @param definitionsFactory the definitions factory for this instance.
*/
public void setDefinitionsFactory(DefinitionsFactory definitionsFactory) {
this.definitionsFactory = definitionsFactory;
}
/**
* Returns the preparer factory used by this container.
*
* @return return the preparerInstance factory used by this container.
*/
public PreparerFactory getPreparerFactory() {
return preparerFactory;
}
/**
* Set the preparerInstance factory. This method first ensures
* that the container has not yet been initialized.
*
* @param preparerFactory the preparerInstance factory for this conainer.
*/
public void setPreparerFactory(PreparerFactory preparerFactory) {
this.preparerFactory = preparerFactory;
}
/**
* Sets the renderer instance factory.
*
* @param rendererFactory the renderer instance factory for this container.
* @since 2.1.0
*/
public void setRendererFactory(RendererFactory rendererFactory) {
this.rendererFactory = rendererFactory;
}
/** {@inheritDoc} */
public void setAttributeEvaluatorFactory(
AttributeEvaluatorFactory attributeEvaluatorFactory) {
this.attributeEvaluatorFactory = attributeEvaluatorFactory;
}
/** {@inheritDoc} */
public void prepare(String preparer, Request request) {
prepare(request, preparer, false);
}
/** {@inheritDoc} */
public void render(String definitionName, Request request) {
log.debug("Render request received for definition '{}'", definitionName);
Definition definition = getDefinition(definitionName, request);
if (definition == null) {
throw new NoSuchDefinitionException("Unable to find the definition '" + definitionName + "'");
}
render(definition, request);
}
/**
* Renders the specified definition.
* @param definition The definition to render.
* @param request The request context.
* @since 2.1.3
*/
public void render(Definition definition, Request request) {
AttributeContext originalContext = getAttributeContext(request);
BasicAttributeContext subContext = new BasicAttributeContext(definition);
subContext.inherit(originalContext);
pushContext(subContext, request);
try {
render(request, subContext);
} finally {
popContext(request);
}
}
/** {@inheritDoc} */
public void render(Attribute attr, Request request)
throws IOException {
if (attr == null) {
throw new CannotRenderException("Cannot render a null attribute");
}
if (attr.isPermitted(request)) {
Renderer renderer = rendererFactory.getRenderer(attr.getRenderer());
Object value = evaluate(attr, request);
if (!(value instanceof String)) {
throw new CannotRenderException(
"Cannot render an attribute that is not a string, toString returns: "
+ value);
}
renderer.render((String) value, request);
}
}
/** {@inheritDoc} */
public Object evaluate(Attribute attribute, Request request) {
AttributeEvaluator evaluator = attributeEvaluatorFactory
.getAttributeEvaluator(attribute);
return evaluator.evaluate(attribute, request);
}
/** {@inheritDoc} */
public boolean isValidDefinition(String definitionName, Request request) {
try {
Definition definition = getDefinition(definitionName, request);
return definition != null;
} catch (NoSuchDefinitionException nsde) {
log.debug("Cannot find definition '{}'", definitionName);
log.debug("Exception related to the not found definition", nsde);
return false;
}
}
/** {@inheritDoc} */
@Override
public Definition getDefinition(String definitionName,
Request request) {
Definition definition =
definitionsFactory.getDefinition(definitionName, request);
return definition;
}
/**
* Returns the context stack.
*
* @param tilesContext The Tiles context object to use.
* @return The needed stack of contexts.
* @since 2.0.6
*/
@SuppressWarnings("unchecked")
protected Deque<AttributeContext> getContextStack(Request tilesContext) {
Map<String, Object> requestScope = tilesContext.getContext("request");
Deque<AttributeContext> contextStack = (Deque<AttributeContext>) requestScope
.get(ATTRIBUTE_CONTEXT_STACK);
if (contextStack == null) {
contextStack = new LinkedList<AttributeContext>();
requestScope.put(ATTRIBUTE_CONTEXT_STACK, contextStack);
}
return contextStack;
}
/**
* Pushes a context object in the stack.
*
* @param context The context to push.
* @param tilesContext The Tiles context object to use.
* @since 2.0.6
*/
protected void pushContext(AttributeContext context,
Request tilesContext) {
Deque<AttributeContext> contextStack = getContextStack(tilesContext);
contextStack.push(context);
}
/**
* Pops a context object out of the stack.
*
* @param tilesContext The Tiles context object to use.
* @return The popped context object.
* @since 2.0.6
*/
protected AttributeContext popContext(Request tilesContext) {
Deque<AttributeContext> contextStack = getContextStack(tilesContext);
return contextStack.pop();
}
/**
* Get attribute context from request.
*
* @param tilesContext current Tiles application context.
* @return BasicAttributeContext or null if context is not found.
* @since 2.0.6
*/
protected AttributeContext getContext(Request tilesContext) {
Deque<AttributeContext> contextStack = getContextStack(tilesContext);
if (!contextStack.isEmpty()) {
return contextStack.peek();
}
return null;
}
/**
* Execute a preparer.
*
* @param context The request context.
* @param preparerName The name of the preparer.
* @param ignoreMissing If <code>true</code> if the preparer is not found,
* it ignores the problem.
* @throws NoSuchPreparerException If the preparer is not found (and
* <code>ignoreMissing</code> is not set) or if the preparer itself threw an
* exception.
*/
private void prepare(Request context, String preparerName, boolean ignoreMissing) {
log.debug("Prepare request received for '{}'", preparerName);
ViewPreparer preparer = preparerFactory.getPreparer(preparerName, context);
if (preparer == null && ignoreMissing) {
return;
}
if (preparer == null) {
throw new NoSuchPreparerException("Preparer '" + preparerName + " not found");
}
AttributeContext attributeContext = getContext(context);
preparer.execute(context, attributeContext);
}
/**
* Renders the specified attribute context.
*
* @param request The request context.
* @param attributeContext The context to render.
* @throws InvalidTemplateException If the template is not valid.
* @throws CannotRenderException If something goes wrong during rendering.
* @since 2.1.3
*/
protected void render(Request request,
AttributeContext attributeContext) {
try {
if (attributeContext.getPreparer() != null) {
prepare(request, attributeContext.getPreparer(), true);
}
render(attributeContext.getTemplateAttribute(), request);
} catch (IOException e) {
throw new CannotRenderException(e.getMessage(), e);
}
}
}