Re-implements tiles integration based on new API

This commit is contained in:
Lukasz Lenart
2015-11-25 20:29:18 +01:00
parent 85b219813f
commit 70716e9452
4 changed files with 359 additions and 100 deletions
@@ -0,0 +1,72 @@
package org.apache.struts2.tiles;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.Container;
import freemarker.template.TemplateException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.views.freemarker.FreemarkerResult;
import org.apache.tiles.Attribute;
import org.apache.tiles.context.TilesRequestContext;
import org.apache.tiles.impl.InvalidTemplateException;
import org.apache.tiles.renderer.impl.AbstractTypeDetectingAttributeRenderer;
import org.apache.tiles.servlet.context.ServletTilesRequestContext;
import org.apache.tiles.servlet.context.ServletUtil;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class StrutsFreeMarkerAttributeRenderer extends AbstractTypeDetectingAttributeRenderer {
private static Logger LOG = LogManager.getLogger(StrutsFreeMarkerAttributeRenderer.class);
@Override
public void write(Object value, Attribute attribute, TilesRequestContext request) throws IOException {
if (value != null) {
if (value instanceof String) {
LOG.trace("Rendering freemarker tile ...");
ServletTilesRequestContext servletRequest = ServletUtil.getServletRequest(request);
HttpServletRequest httpRequest = servletRequest.getRequest();
ActionContext ctx = ServletActionContext.getActionContext(httpRequest);
if (ctx == null) {
throw new ConfigurationException("There is no ActionContext for current request!");
}
ActionInvocation invocation = ctx.getActionInvocation();
String include = (String) value;
FreemarkerResult result = new FreemarkerResult(include);
result.setWriter(request.getWriter());
Container container = ctx.getContainer();
container.inject(result);
try {
result.doExecute(include, invocation);
} catch (TemplateException e) {
LOG.error("Exception was thrown during rendering value {}: {}", value, e.getMessage());
throw new InvalidTemplateException(e);
}
} else {
LOG.error("Value {} is not a String, cannot render template!", value);
throw new InvalidTemplateException("Cannot render a template that is not a string: " + String.valueOf(value));
}
} else {
LOG.error("Value is null, cannot render template!");
throw new InvalidTemplateException("Cannot render a null template");
}
}
public boolean isRenderable(Object value, Attribute attribute, TilesRequestContext request) {
if (value instanceof String) {
String string = (String) value;
return string.startsWith("/") && string.endsWith(".ftl");
}
return false;
}
}
@@ -21,73 +21,249 @@
package org.apache.struts2.tiles;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import ognl.PropertyAccessor;
import org.apache.tiles.TilesApplicationContext;
import org.apache.tiles.TilesException;
import org.apache.tiles.context.TilesContextFactory;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.context.ChainedTilesRequestContextFactory;
import org.apache.tiles.context.TilesRequestContext;
import org.apache.tiles.definition.DefinitionsFactory;
import org.apache.tiles.factory.TilesContainerFactory;
import org.apache.tiles.context.TilesRequestContextFactory;
import org.apache.tiles.definition.DefinitionsFactoryException;
import org.apache.tiles.definition.pattern.DefinitionPatternMatcherFactory;
import org.apache.tiles.definition.pattern.PatternDefinitionResolver;
import org.apache.tiles.definition.pattern.PrefixedPatternDefinitionResolver;
import org.apache.tiles.definition.pattern.regexp.RegexpDefinitionPatternMatcherFactory;
import org.apache.tiles.definition.pattern.wildcard.WildcardDefinitionPatternMatcherFactory;
import org.apache.tiles.el.ELAttributeEvaluator;
import org.apache.tiles.el.JspExpressionFactoryFactory;
import org.apache.tiles.el.TilesContextBeanELResolver;
import org.apache.tiles.el.TilesContextELResolver;
import org.apache.tiles.evaluator.AttributeEvaluatorFactory;
import org.apache.tiles.evaluator.BasicAttributeEvaluatorFactory;
import org.apache.tiles.evaluator.impl.DirectAttributeEvaluator;
import org.apache.tiles.factory.BasicTilesContainerFactory;
import org.apache.tiles.factory.TilesContainerFactoryException;
import org.apache.tiles.freemarker.context.FreeMarkerTilesRequestContextFactory;
import org.apache.tiles.impl.BasicTilesContainer;
import org.apache.tiles.preparer.PreparerFactory;
import org.apache.tiles.impl.mgmt.CachingTilesContainer;
import org.apache.tiles.locale.LocaleResolver;
import org.apache.tiles.ognl.ApplicationScopeNestedObjectExtractor;
import org.apache.tiles.ognl.DelegatePropertyAccessor;
import org.apache.tiles.ognl.NestedObjectDelegatePropertyAccessor;
import org.apache.tiles.ognl.OGNLAttributeEvaluator;
import org.apache.tiles.ognl.PropertyAccessorDelegateFactory;
import org.apache.tiles.ognl.RequestScopeNestedObjectExtractor;
import org.apache.tiles.ognl.SessionScopeNestedObjectExtractor;
import org.apache.tiles.ognl.TilesApplicationContextNestedObjectExtractor;
import org.apache.tiles.ognl.TilesContextPropertyAccessorDelegateFactory;
import org.apache.tiles.renderer.AttributeRenderer;
import org.apache.tiles.renderer.TypeDetectingAttributeRenderer;
import org.apache.tiles.renderer.impl.BasicRendererFactory;
import org.apache.tiles.renderer.impl.ChainedDelegateAttributeRenderer;
import org.apache.tiles.util.URLUtil;
import javax.el.ArrayELResolver;
import javax.el.BeanELResolver;
import javax.el.CompositeELResolver;
import javax.el.ELResolver;
import javax.el.ListELResolver;
import javax.el.MapELResolver;
import javax.el.ResourceBundleELResolver;
import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class StrutsTilesContainerFactory extends TilesContainerFactory {
@Override
protected void storeContainerDependencies(Object context, Map<String, String> initParameters, Map<String, String> configuration, BasicTilesContainer container) throws TilesException {
TilesContextFactory contextFactory =
(TilesContextFactory) createFactory(configuration,
CONTEXT_FACTORY_INIT_PARAM);
contextFactory = new StrutsTilesContextFactory(contextFactory);
DefinitionsFactory defsFactory =
(DefinitionsFactory) createFactory(configuration,
DEFINITIONS_FACTORY_INIT_PARAM);
PreparerFactory prepFactory =
(PreparerFactory) createFactory(configuration,
PREPARER_FACTORY_INIT_PARAM);
contextFactory.init(configuration);
TilesApplicationContext tilesContext =
contextFactory.createApplicationContext(context);
container.setDefinitionsFactory(defsFactory);
container.setContextFactory(contextFactory);
container.setPreparerFactory(prepFactory);
container.setApplicationContext(tilesContext);
}
/**
* Dedicated Struts factory to build Tiles container with support for:
* - Freemarker
* - OGNL (as default)
* - EL
* - Wildcards
*
* If you need additional features create your own listener and factory,
* you can base on code from Tiles' CompleteAutoloadTilesContainerFactory
*/
public class StrutsTilesContainerFactory extends BasicTilesContainerFactory {
/**
* Wrapper factory, used to decorate the TilesRequestContext with a
* FreemarkerResult aware version.
*
* The freemarker renderer name.
*/
class StrutsTilesContextFactory implements TilesContextFactory {
public static final String FREEMARKER_RENDERER_NAME = "freemarker";
private TilesContextFactory factory;
/**
* Supported pattern types
*/
public static final String PATTERN_WILDCARD = "WILDCARD";
public static final String PATTERN_REGEXP = "REGEXP";
public StrutsTilesContextFactory(TilesContextFactory factory) {
this.factory = factory;
}
@Override
protected BasicTilesContainer instantiateContainer(TilesApplicationContext applicationContext) {
return new CachingTilesContainer();
}
public void init(Map<String, String> map) {
factory.init(map);
}
@Override
protected List<TilesRequestContextFactory> getTilesRequestContextFactoriesToBeChained(ChainedTilesRequestContextFactory parent) {
public TilesApplicationContext createApplicationContext(Object context) {
return factory.createApplicationContext(context);
}
List<TilesRequestContextFactory> factories = super.getTilesRequestContextFactoriesToBeChained(parent);
public TilesRequestContext createRequestContext(
TilesApplicationContext tilesApplicationContext,
Object... requestItems) {
TilesRequestContext context = factory.createRequestContext(tilesApplicationContext, requestItems);
return new StrutsTilesRequestContext(context);
registerRequestContextFactory(FreeMarkerTilesRequestContextFactory.class.getName(), factories, parent);
return factories;
}
@Override
protected void registerAttributeRenderers(
BasicRendererFactory rendererFactory,
TilesApplicationContext applicationContext,
TilesRequestContextFactory contextFactory,
TilesContainer container,
AttributeEvaluatorFactory attributeEvaluatorFactory) {
super.registerAttributeRenderers(
rendererFactory,
applicationContext,
contextFactory,
container,
attributeEvaluatorFactory);
StrutsFreeMarkerAttributeRenderer freemarkerRenderer = new StrutsFreeMarkerAttributeRenderer();
freemarkerRenderer.setApplicationContext(applicationContext);
freemarkerRenderer.setAttributeEvaluatorFactory(attributeEvaluatorFactory);
freemarkerRenderer.setRequestContextFactory(contextFactory);
rendererFactory.registerRenderer(FREEMARKER_RENDERER_NAME, freemarkerRenderer);
}
@Override
protected AttributeRenderer createDefaultAttributeRenderer(
BasicRendererFactory rendererFactory,
TilesApplicationContext applicationContext,
TilesRequestContextFactory contextFactory,
TilesContainer container,
AttributeEvaluatorFactory attributeEvaluatorFactory) {
ChainedDelegateAttributeRenderer retValue = new ChainedDelegateAttributeRenderer();
retValue.addAttributeRenderer((TypeDetectingAttributeRenderer) rendererFactory
.getRenderer(DEFINITION_RENDERER_NAME));
retValue.addAttributeRenderer((TypeDetectingAttributeRenderer) rendererFactory
.getRenderer(FREEMARKER_RENDERER_NAME));
retValue.addAttributeRenderer((TypeDetectingAttributeRenderer) rendererFactory
.getRenderer(TEMPLATE_RENDERER_NAME));
retValue.addAttributeRenderer((TypeDetectingAttributeRenderer) rendererFactory
.getRenderer(STRING_RENDERER_NAME));
retValue.setApplicationContext(applicationContext);
retValue.setRequestContextFactory(contextFactory);
retValue.setAttributeEvaluatorFactory(attributeEvaluatorFactory);
return retValue;
}
@Override
protected AttributeEvaluatorFactory createAttributeEvaluatorFactory(
TilesApplicationContext applicationContext,
TilesRequestContextFactory contextFactory,
LocaleResolver resolver) {
BasicAttributeEvaluatorFactory attributeEvaluatorFactory = new BasicAttributeEvaluatorFactory(new DirectAttributeEvaluator());
attributeEvaluatorFactory.registerAttributeEvaluator("OGNL", createOGNLEvaluator());
attributeEvaluatorFactory.registerAttributeEvaluator("EL", createELEvaluator(applicationContext));
return attributeEvaluatorFactory;
}
@Override
protected <T> PatternDefinitionResolver<T> createPatternDefinitionResolver(Class<T> customizationKeyClass) {
DefinitionPatternMatcherFactory wildcardFactory = new WildcardDefinitionPatternMatcherFactory();
DefinitionPatternMatcherFactory regexpFactory = new RegexpDefinitionPatternMatcherFactory();
PrefixedPatternDefinitionResolver<T> resolver = new PrefixedPatternDefinitionResolver<>();
resolver.registerDefinitionPatternMatcherFactory(PATTERN_WILDCARD, wildcardFactory);
resolver.registerDefinitionPatternMatcherFactory(PATTERN_REGEXP, regexpFactory);
return resolver;
}
@Override
protected List<URL> getSourceURLs(TilesApplicationContext applicationContext,
TilesRequestContextFactory contextFactory) {
try {
Set<URL> finalSet = new HashSet<>();
Set<URL> webINFSet = applicationContext.getResources("/WEB-INF/**/tiles*.xml");
Set<URL> metaINFSet = applicationContext.getResources("classpath*:META-INF/**/tiles*.xml");
if (webINFSet != null) {
finalSet.addAll(webINFSet);
}
if (metaINFSet != null) {
finalSet.addAll(metaINFSet);
}
return URLUtil.getBaseTilesDefinitionURLs(finalSet);
} catch (IOException e) {
throw new DefinitionsFactoryException("Cannot load definition URLs", e);
}
}
}
protected ELAttributeEvaluator createELEvaluator(TilesApplicationContext applicationContext) {
ELAttributeEvaluator evaluator = new ELAttributeEvaluator();
evaluator.setApplicationContext(applicationContext);
JspExpressionFactoryFactory efFactory = new JspExpressionFactoryFactory();
efFactory.setApplicationContext(applicationContext);
evaluator.setExpressionFactory(efFactory.getExpressionFactory());
ELResolver elResolver = new CompositeELResolver() {
{
add(new TilesContextELResolver());
add(new TilesContextBeanELResolver());
add(new ArrayELResolver(false));
add(new ListELResolver(false));
add(new MapELResolver(false));
add(new ResourceBundleELResolver());
add(new BeanELResolver(false));
}
};
evaluator.setResolver(elResolver);
return evaluator;
}
protected OGNLAttributeEvaluator createOGNLEvaluator() {
try {
PropertyAccessor objectPropertyAccessor = OgnlRuntime.getPropertyAccessor(Object.class);
PropertyAccessor mapPropertyAccessor = OgnlRuntime.getPropertyAccessor(Map.class);
PropertyAccessor applicationContextPropertyAccessor =
new NestedObjectDelegatePropertyAccessor<>(
new TilesApplicationContextNestedObjectExtractor(),
objectPropertyAccessor);
PropertyAccessor requestScopePropertyAccessor =
new NestedObjectDelegatePropertyAccessor<>(
new RequestScopeNestedObjectExtractor(), mapPropertyAccessor);
PropertyAccessor sessionScopePropertyAccessor =
new NestedObjectDelegatePropertyAccessor<>(
new SessionScopeNestedObjectExtractor(), mapPropertyAccessor);
PropertyAccessor applicationScopePropertyAccessor =
new NestedObjectDelegatePropertyAccessor<>(
new ApplicationScopeNestedObjectExtractor(), mapPropertyAccessor);
PropertyAccessorDelegateFactory<TilesRequestContext> factory =
new TilesContextPropertyAccessorDelegateFactory(
objectPropertyAccessor, applicationContextPropertyAccessor,
requestScopePropertyAccessor, sessionScopePropertyAccessor,
applicationScopePropertyAccessor);
PropertyAccessor tilesRequestAccessor = new DelegatePropertyAccessor<>(factory);
OgnlRuntime.setPropertyAccessor(TilesRequestContext.class, tilesRequestAccessor);
return new OGNLAttributeEvaluator();
} catch (OgnlException e) {
throw new TilesContainerFactoryException("Cannot initialize OGNL evaluator", e);
}
}
}
@@ -0,0 +1,47 @@
/*
* 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.struts2.tiles;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tiles.TilesApplicationContext;
import org.apache.tiles.factory.AbstractTilesContainerFactory;
import org.apache.tiles.servlet.wildcard.WildcardServletTilesApplicationContext;
import org.apache.tiles.startup.AbstractTilesInitializer;
import javax.servlet.ServletContext;
public class StrutsTilesInitializer extends AbstractTilesInitializer {
private static final Logger LOG = LogManager.getLogger(StrutsTilesInitializer.class);
@Override
protected TilesApplicationContext createTilesApplicationContext(TilesApplicationContext preliminaryContext) {
LOG.debug("Initializing Tiles wildcard support ...");
return new WildcardServletTilesApplicationContext((ServletContext) preliminaryContext.getContext());
}
@Override
protected AbstractTilesContainerFactory createContainerFactory(TilesApplicationContext context) {
LOG.trace("Creating dedicated Struts factory to create Tiles container");
return new StrutsTilesContainerFactory();
}
}
@@ -1,6 +1,4 @@
/*
* $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
@@ -21,57 +19,23 @@
package org.apache.struts2.tiles;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.TilesException;
import org.apache.tiles.factory.TilesContainerFactory;
import org.apache.tiles.web.startup.TilesListener;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tiles.startup.TilesInitializer;
import org.apache.tiles.web.startup.AbstractTilesListener;
/**
* Listener used to automatically inject ServletContext
* init parameters so that they don't need to be configured
* explicitly for tiles integration. This is provided
* mainly for backwards compatibility with Struts 2.0.1
* configuration.
* Listener used to automatically tie Tiles support into Struts
*
* @since Struts 2.0.2
* @version $Rev$
*
*/
public class StrutsTilesListener extends TilesListener {
public class StrutsTilesListener extends AbstractTilesListener {
private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
private static final Map<String, String> INIT;
static {
INIT = new HashMap<String, String>();
INIT.put(TilesContainerFactory.CONTAINER_FACTORY_INIT_PARAM,
StrutsTilesContainerFactory.class.getName());
@Override
protected TilesInitializer createTilesInitializer() {
LOG.info("Starting Struts Tiles 2 integration ...");
return new StrutsTilesInitializer();
}
protected TilesContainer createContainer(ServletContext context)
throws TilesException {
if(context.getInitParameter(TilesContainerFactory.CONTEXT_FACTORY_INIT_PARAM) == null) {
context = decorate(context);
}
else {
if (LOG.isWarnEnabled()) {
LOG.warn("Tiles container factory is explicitly set. Not injecting struts configuration.");
}
}
return super.createContainer(context);
}
protected ServletContext decorate(ServletContext context) {
return new ConfiguredServletContext(context, INIT);
}
}
}