diff --git a/plugins/portlet-tiles/pom.xml b/plugins/portlet-tiles/pom.xml index c2793d769..333c5701a 100644 --- a/plugins/portlet-tiles/pom.xml +++ b/plugins/portlet-tiles/pom.xml @@ -42,6 +42,10 @@ org.apache.struts struts2-portlet-plugin + + org.apache.tiles + tiles-portlet + javax.servlet jsp-api diff --git a/plugins/portlet-tiles/src/main/java/org/apache/struts2/views/tiles/PortletTilesResult.java b/plugins/portlet-tiles/src/main/java/org/apache/struts2/views/tiles/PortletTilesResult.java index c08a99bb3..3ca68fe5e 100644 --- a/plugins/portlet-tiles/src/main/java/org/apache/struts2/views/tiles/PortletTilesResult.java +++ b/plugins/portlet-tiles/src/main/java/org/apache/struts2/views/tiles/PortletTilesResult.java @@ -1,3 +1,24 @@ +/* + * $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.struts2.views.tiles; import com.opensymphony.xwork2.ActionInvocation; @@ -9,6 +30,7 @@ import org.apache.struts2.portlet.context.PortletActionContext; import org.apache.tiles.TilesContainer; import org.apache.tiles.TilesException; import org.apache.tiles.access.TilesAccess; +import org.apache.tiles.portlet.context.PortletUtil; import javax.portlet.ActionResponse; import javax.portlet.PortletException; @@ -19,11 +41,13 @@ import java.io.IOException; import java.util.Map; /** - * JIRA WW-2749 (STRUTS). + * Dedicated Tile result to be used in Portlet environment + * + * WW-2749 */ public class PortletTilesResult extends ServletDispatcherResult { - private static final long serialVersionUID = -3806939435493086244L; + public static final String TILES_ACTION_NAME = "tilesDirect"; public PortletTilesResult() { super(); @@ -33,10 +57,7 @@ public class PortletTilesResult extends ServletDispatcherResult { super(location); } - // FIXME PATCH du JIRA WW-2749 (STRUTS) - public void doExecute(String location, ActionInvocation invocation) - throws IOException, TemplateException, PortletException, TilesException { - + public void doExecute(String location, ActionInvocation invocation) throws Exception { if (PortletActionContext.getPhase().isAction() || PortletActionContext.getPhase().isEvent()) { executeActionResult(location, invocation); } else { @@ -44,15 +65,10 @@ public class PortletTilesResult extends ServletDispatcherResult { } } - /** - * @param location - * @throws TilesException - */ protected void executeRenderResult(String location) throws TilesException { setLocation(location); - ServletContext servletContext = ServletActionContext.getServletContext(); - TilesContainer container = TilesAccess.getContainer(servletContext); + TilesContainer container = PortletUtil.getContainer(PortletActionContext.getPortletContext()); HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); @@ -60,14 +76,10 @@ public class PortletTilesResult extends ServletDispatcherResult { container.render(location, request, response); } - /** - * @param location - * @param invocation - */ protected void executeActionResult(String location, ActionInvocation invocation) { ActionResponse res = PortletActionContext.getActionResponse(); - res.setRenderParameter(PortletConstants.ACTION_PARAM, "tilesDirect"); + res.setRenderParameter(PortletConstants.ACTION_PARAM, TILES_ACTION_NAME); Map sessionMap = invocation.getInvocationContext().getSession(); sessionMap.put(PortletConstants.RENDER_DIRECT_LOCATION, location); diff --git a/plugins/tiles/pom.xml b/plugins/tiles/pom.xml index 9114b2b4c..4f9d440a6 100644 --- a/plugins/tiles/pom.xml +++ b/plugins/tiles/pom.xml @@ -38,17 +38,32 @@ org.apache.tiles tiles-core + + org.apache.tiles + tiles-servlet + org.apache.tiles tiles-jsp runtime - javax.servlet - jsp-api - provided + org.apache.tiles + tiles-freemarker + + + org.apache.tiles + tiles-ognl + + + org.apache.tiles + tiles-el + + + org.glassfish + javax.el + true - UTF-8 diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/ConfiguredServletContext.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/ConfiguredServletContext.java deleted file mode 100644 index dfc1408de..000000000 --- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/ConfiguredServletContext.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * $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.struts2.tiles; - - -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.*; - -/** - * ServletContext implementation which allows Struts - * to inject initialization parameters into the context - * in order to reduce the amount of configuration required - * within web.xml for using Tiles. - * - * The specified init parameters are only utilized if - * they are not explicitaly defined in the web.xml - * - * @version $Rev$ - * @since Struts 2.0.1 - */ -@SuppressWarnings("deprecation") -public class ConfiguredServletContext implements ServletContext { - - private ServletContext rootContext; - private Map initParameters; - - - public ConfiguredServletContext(ServletContext context, Map initParameters) { - this.rootContext = context; - this.initParameters = initParameters; - } - - public ServletContext getContext(String string) { - return rootContext.getContext(string); - } - - public int getMajorVersion() { - return rootContext.getMajorVersion(); - } - - public int getMinorVersion() { - return rootContext.getMinorVersion(); - } - - public String getMimeType(String string) { - return rootContext.getMimeType(string); - } - - public Set getResourcePaths(String string) { - return rootContext.getResourcePaths(string); - } - - public URL getResource(String string) throws MalformedURLException { - return rootContext.getResource(string); - } - - public InputStream getResourceAsStream(String string) { - return rootContext.getResourceAsStream(string); - } - - public RequestDispatcher getRequestDispatcher(String string) { - return rootContext.getRequestDispatcher(string); - } - - public RequestDispatcher getNamedDispatcher(String string) { - return rootContext.getNamedDispatcher(string); - } - - @SuppressWarnings("deprecation") - public Servlet getServlet(String string) throws ServletException { - return rootContext.getServlet(string); - } - - @SuppressWarnings("deprecation") - public Enumeration getServlets() { - return rootContext.getServlets(); //To change body of implemented methods use File | Settings | File Templates. - } - - @SuppressWarnings("deprecation") - public Enumeration getServletNames() { - return rootContext.getServletNames(); - } - - public void log(String string) { - rootContext.log(string); - } - - @SuppressWarnings("deprecation") - public void log(Exception exception, String string) { - rootContext.log(exception, string); - } - - public void log(String string, Throwable throwable) { - rootContext.log(string, throwable); - } - - public String getRealPath(String string) { - return rootContext.getRealPath(string); - } - - public String getServerInfo() { - return rootContext.getServerInfo(); - } - - public String getInitParameter(String string) { - String parm = rootContext.getInitParameter(string); - if (parm == null) { - return initParameters.get(string); - } - return parm; - } - - public Enumeration getInitParameterNames() { - return new CompositeEnumeration( - rootContext.getInitParameterNames(), - initParameters.keySet().iterator()); - } - - public Object getAttribute(String string) { - return rootContext.getAttribute(string); - } - - public Enumeration getAttributeNames() { - return rootContext.getAttributeNames(); - } - - public void setAttribute(String string, Object object) { - rootContext.setAttribute(string, object); - } - - public void removeAttribute(String string) { - rootContext.removeAttribute(string); - } - - public String getServletContextName() { - return rootContext.getServletContextName(); - } - - class CompositeEnumeration implements Enumeration { - - private Enumeration first; - private Iterator second; - - - public CompositeEnumeration(Enumeration first, Iterator second) { - this.first = first; - this.second = second; - } - - public boolean hasMoreElements() { - return first.hasMoreElements() || second.hasNext(); - } - - public Object nextElement() { - if (first.hasMoreElements()) { - return first.nextElement(); - } - - return second.next(); - } - } -} diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsFreeMarkerAttributeRenderer.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsFreeMarkerAttributeRenderer.java new file mode 100644 index 000000000..c9491b4db --- /dev/null +++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsFreeMarkerAttributeRenderer.java @@ -0,0 +1,116 @@ +/* + * 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 com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.config.ConfigurationException; +import com.opensymphony.xwork2.inject.Container; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import freemarker.ext.beans.BeanModel; +import freemarker.template.Configuration; +import freemarker.template.TemplateException; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.views.JspSupportServlet; +import org.apache.struts2.views.freemarker.FreemarkerManager; +import org.apache.struts2.views.freemarker.FreemarkerResult; +import org.apache.struts2.views.freemarker.StrutsBeanWrapper; +import org.apache.tiles.Attribute; +import org.apache.tiles.context.TilesRequestContext; +import org.apache.tiles.freemarker.template.TilesFMModelRepository; +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.ServletContext; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +public class StrutsFreeMarkerAttributeRenderer extends AbstractTypeDetectingAttributeRenderer { + + private static Logger LOG = LoggerFactory.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!"); + } + + registerTilesBeanModel(ctx); + + String include = (String) value; + FreemarkerResult result = new FreemarkerResult(include); + result.setWriter(request.getWriter()); + + Container container = ctx.getContainer(); + container.inject(result); + + try { + ActionInvocation invocation = ctx.getActionInvocation(); + 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; + } + + /** + * This register dedicated BeanModel to support tiles tags. + * It requires {@link org.apache.struts2.views.JspSupportServlet} to be registered in web.xml + */ + protected void registerTilesBeanModel(ActionContext ctx) { + ServletContext servletContext = ServletActionContext.getServletContext(); + Configuration configuration = ctx.getInstance(FreemarkerManager.class).getConfiguration(servletContext); + + StrutsBeanWrapper wrapper = (StrutsBeanWrapper) ctx.getInstance(FreemarkerManager.class).getWrapper(); + + LOG.trace("Adding support for Tiles tags, please remember to register {} in web.xml!", JspSupportServlet.class.getName()); + + BeanModel tilesBeanModel = new BeanModel(new TilesFMModelRepository(), wrapper); + configuration.setSharedVariable("tiles", tilesBeanModel); + } + +} diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java index 491ee02ab..b96884cb6 100644 --- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java +++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java @@ -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,73 +19,256 @@ 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.context.TilesRequestContextFactory; import org.apache.tiles.definition.DefinitionsFactory; -import org.apache.tiles.factory.TilesContainerFactory; +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.servlet.context.ServletUtil; +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 javax.servlet.ServletContext; +import java.io.IOException; +import java.net.URL; +import java.util.List; import java.util.Map; +import java.util.Set; - -public class StrutsTilesContainerFactory extends TilesContainerFactory { - - - @Override - protected void storeContainerDependencies(Object context, Map initParameters, Map 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; - } + /** + * Default pattern to be used to collect Tiles definitions if user didn't configure any + */ + public static final String TILES_DEFAULT_PATTERN = "tiles*.xml"; - public void init(Map map) { - factory.init(map); - } + @Override + protected BasicTilesContainer instantiateContainer(TilesApplicationContext applicationContext) { + CachingTilesContainer tilesContainer = new CachingTilesContainer(); + ServletContext servletContext = (ServletContext) applicationContext.getContext(); + ServletUtil.setContainer(servletContext, tilesContainer); + return tilesContainer; + } - public TilesApplicationContext createApplicationContext(Object context) { - return factory.createApplicationContext(context); - } + @Override + protected List getTilesRequestContextFactoriesToBeChained(ChainedTilesRequestContextFactory parent) { - public TilesRequestContext createRequestContext( - TilesApplicationContext tilesApplicationContext, - Object... requestItems) { - TilesRequestContext context = factory.createRequestContext(tilesApplicationContext, requestItems); - return new StrutsTilesRequestContext(context); + List factories = super.getTilesRequestContextFactoriesToBeChained(parent); + + 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 PatternDefinitionResolver createPatternDefinitionResolver(Class customizationKeyClass) { + + DefinitionPatternMatcherFactory wildcardFactory = new WildcardDefinitionPatternMatcherFactory(); + DefinitionPatternMatcherFactory regexpFactory = new RegexpDefinitionPatternMatcherFactory(); + + PrefixedPatternDefinitionResolver resolver = new PrefixedPatternDefinitionResolver(); + resolver.registerDefinitionPatternMatcherFactory(PATTERN_WILDCARD, wildcardFactory); + resolver.registerDefinitionPatternMatcherFactory(PATTERN_REGEXP, regexpFactory); + + return resolver; + } + + @Override + protected List getSourceURLs(TilesApplicationContext applicationContext, TilesRequestContextFactory contextFactory) { + try { + Set finalSet = applicationContext.getResources(getTilesDefinitionPattern(applicationContext.getInitParams())); + + return URLUtil.getBaseTilesDefinitionURLs(finalSet); + } catch (IOException e) { + throw new DefinitionsFactoryException("Cannot load definition URLs", e); } } -} + + protected String getTilesDefinitionPattern(Map params) { + if (params.containsKey(DefinitionsFactory.DEFINITIONS_CONFIG)) { + return params.get(DefinitionsFactory.DEFINITIONS_CONFIG); + } + return TILES_DEFAULT_PATTERN; + } + + 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 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); + } + } + +} \ No newline at end of file diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesInitializer.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesInitializer.java new file mode 100644 index 000000000..550c6b18d --- /dev/null +++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesInitializer.java @@ -0,0 +1,46 @@ +/* + * 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 com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.tiles.TilesApplicationContext; +import org.apache.tiles.factory.AbstractTilesContainerFactory; +import org.apache.tiles.startup.AbstractTilesInitializer; + +import javax.servlet.ServletContext; + +public class StrutsTilesInitializer extends AbstractTilesInitializer { + + private static final Logger LOG = LoggerFactory.getLogger(StrutsTilesInitializer.class); + + @Override + protected TilesApplicationContext createTilesApplicationContext(TilesApplicationContext preliminaryContext) { + LOG.debug("Initializing Tiles wildcard support ..."); + return new StrutsWildcardServletTilesApplicationContext((ServletContext) preliminaryContext.getContext()); + } + + @Override + protected AbstractTilesContainerFactory createContainerFactory(TilesApplicationContext context) { + LOG.trace("Creating dedicated Struts factory to create Tiles container"); + return new StrutsTilesContainerFactory(); + } + +} diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java index b2213ca7e..c55a8bfdc 100644 --- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java +++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java @@ -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 com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; +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 = LoggerFactory.getLogger(StrutsTilesListener.class); - private static final Map INIT; - - static { - INIT = new HashMap(); - 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); - } - -} +} \ No newline at end of file diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesRequestContext.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesRequestContext.java deleted file mode 100644 index 856517c03..000000000 --- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesRequestContext.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * $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.struts2.tiles; - -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.ActionInvocation; -import com.opensymphony.xwork2.inject.Container; -import com.opensymphony.xwork2.util.logging.Logger; -import com.opensymphony.xwork2.util.logging.LoggerFactory; -import org.apache.struts2.ServletActionContext; -import org.apache.struts2.views.freemarker.FreemarkerResult; -import org.apache.tiles.context.TilesRequestContext; -import org.apache.tiles.context.TilesRequestContextWrapper; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Default implementation of TilesUtil. - * This class contains default implementation of utilities. This implementation - * is intended to be used without Struts. - *

- * TilesUtilImpl implementation used to intercept .ftl requests and - * ensure that they are setup properly to take advantage of the - * {@link FreemarkerResult}. - * - * @version $Id$ - */ -public class StrutsTilesRequestContext extends TilesRequestContextWrapper { - - private static final Logger LOG = LoggerFactory.getLogger(StrutsTilesRequestContext.class); - - /** - * The mask used to detect requests which should be intercepted. - */ - private String mask; - - /** - * Default constructor. - * Sets the mask to '.ftl' - * - * @param context - */ - public StrutsTilesRequestContext(TilesRequestContext context) { - this(context, ".ftl"); - } - - /** - * Optional constructor used to specify a specific mask. - * - * @param mask - * @param context - */ - public StrutsTilesRequestContext(TilesRequestContext context, String mask) { - super(context); - this.mask = mask; - } - - public void dispatch(String include) throws IOException { - if (include.endsWith(mask)) { - // FIXME This way FreeMarker results still don't have a content-type! - include(include); - } else { - super.dispatch(include); - } - } - - /** - * Enhancement of the default include which allows for freemarker - * templates to be intercepted so that the FreemarkerResult can - * be used in order to setup the appropriate model. - * - * @throws IOException - */ - public void include(String include) throws IOException { - if (include.endsWith(mask)) { - if (LOG.isDebugEnabled()) { - LOG.debug("Intercepting tiles include '" + include + "'. Processing as freemarker result."); - } - HttpServletRequest request = (HttpServletRequest) getRequest(); - HttpServletResponse response = (HttpServletResponse) getResponse(); - - ActionContext ctx = ServletActionContext.getActionContext(request); - ActionInvocation invocation = ctx.getActionInvocation(); - - try { - FreemarkerResult result = new FreemarkerResult(); - result.setWriter(response.getWriter()); - - Container container = ctx.getContainer(); - container.inject(result); - - result.doExecute(include, invocation); - } catch (Exception e) { - LOG.error("Error invoking Freemarker template", e); - throw new IOException("Error invoking Freemarker template." + e.getMessage()); - } - } else { - super.include(include); - } - } - -} diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletTilesApplicationContext.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletTilesApplicationContext.java new file mode 100644 index 000000000..f01b45afb --- /dev/null +++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsWildcardServletTilesApplicationContext.java @@ -0,0 +1,106 @@ +/* + * 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 com.opensymphony.xwork2.config.ConfigurationException; +import com.opensymphony.xwork2.util.WildcardUtil; +import com.opensymphony.xwork2.util.finder.ResourceFinder; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.tiles.servlet.context.ServletTilesApplicationContext; + +import javax.servlet.ServletContext; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +public class StrutsWildcardServletTilesApplicationContext extends ServletTilesApplicationContext { + + private static final Logger LOG = LoggerFactory.getLogger(StrutsWildcardServletTilesApplicationContext.class); + + private ResourceFinder finder; + + public StrutsWildcardServletTilesApplicationContext(ServletContext context) { + super(context); + + Set urls = new HashSet(); + + for (Object path : context.getResourcePaths("/")) { + try { + URL url = new File(context.getRealPath(String.valueOf(path))).toURI().toURL(); + urls.add(url); + } catch (MalformedURLException e) { + throw new ConfigurationException(e); + } + } + + try { + Enumeration resources = getClass().getClassLoader().getResources("/"); + while (resources.hasMoreElements()) { + URL resource = resources.nextElement(); + urls.add(resource); + } + } catch (IOException e) { + throw new ConfigurationException(e); + } + + finder = new ResourceFinder(urls.toArray(new URL[urls.size()])); + } + + public Set getResources(String path) throws IOException { + Set resources = new HashSet(); + + if (path.startsWith("/")) { + LOG.trace("Using ServletContext to load resource #0", path); + URL resource = getResource(path); + if (resource != null) { + resources.add(resource); + } + } + resources.addAll(findResources(path)); + + return resources; + } + + protected Set findResources(String path) throws IOException { + Set resources = new HashSet(); + + LOG.trace("Using ResourceFinder to find matches for #0", path); + + Pattern pattern = WildcardUtil.compileWildcardPattern(path); + Map matches = finder.getResourcesMap(""); + + for (String resource : matches.keySet()) { + if (pattern.matcher(resource).matches()) { + resources.add(matches.get(resource)); + } + } + + LOG.trace("Found resources #0 for path #1", resources, path); + return resources; + } + +} diff --git a/plugins/tiles/src/main/java/org/apache/struts2/views/tiles/TilesResult.java b/plugins/tiles/src/main/java/org/apache/struts2/views/tiles/TilesResult.java index 6135302a9..1e6267299 100644 --- a/plugins/tiles/src/main/java/org/apache/struts2/views/tiles/TilesResult.java +++ b/plugins/tiles/src/main/java/org/apache/struts2/views/tiles/TilesResult.java @@ -28,9 +28,9 @@ import javax.servlet.http.HttpServletResponse; import org.apache.struts2.ServletActionContext; import org.apache.struts2.dispatcher.ServletDispatcherResult; import org.apache.tiles.TilesContainer; -import org.apache.tiles.access.TilesAccess; import com.opensymphony.xwork2.ActionInvocation; +import org.apache.tiles.servlet.context.ServletUtil; /** * @@ -84,6 +84,7 @@ public class TilesResult extends ServletDispatcherResult { public TilesResult(String location) { super(location); } + /** * Dispatches to the given location. Does its forward via a RequestDispatcher. If the * dispatch fails a 404 error will be sent back in the http response. @@ -97,7 +98,8 @@ public class TilesResult extends ServletDispatcherResult { setLocation(location); ServletContext servletContext = ServletActionContext.getServletContext(); - TilesContainer container = TilesAccess.getContainer(servletContext); + + TilesContainer container = ServletUtil.getContainer(servletContext); HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); diff --git a/pom.xml b/pom.xml index 30ebfa89e..afa95f2f7 100644 --- a/pom.xml +++ b/pom.xml @@ -89,8 +89,8 @@ 3.0.13 3.3 5.0.2 - 2.0.6 - 2.2 + 2.2.2 + 2.3 ${project.build.directory}/site @@ -640,6 +640,12 @@ provided + + org.glassfish + javax.el + 3.0.0 + + taglibs standard @@ -666,12 +672,37 @@ tiles-core ${tiles.version} + + org.apache.tiles + tiles-servlet + ${tiles.version} + + + org.apache.tiles + tiles-portlet + ${tiles.version} + org.apache.tiles tiles-jsp ${tiles.version} runtime + + org.apache.tiles + tiles-freemarker + ${tiles.version} + + + org.apache.tiles + tiles-ognl + ${tiles.version} + + + org.apache.tiles + tiles-el + ${tiles.version} + javax.servlet