diff --git a/apps/showcase/src/main/java/org/apache/struts2/showcase/tiles/TilesAnnotationsAction.java b/apps/showcase/src/main/java/org/apache/struts2/showcase/tiles/TilesAnnotationsAction.java
new file mode 100644
index 000000000..2b789c2d8
--- /dev/null
+++ b/apps/showcase/src/main/java/org/apache/struts2/showcase/tiles/TilesAnnotationsAction.java
@@ -0,0 +1,40 @@
+/*
+ * 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.showcase.tiles;
+
+import org.apache.struts2.convention.annotation.Namespace;
+import org.apache.struts2.convention.annotation.ParentPackage;
+import org.apache.struts2.convention.annotation.Result;
+import org.apache.struts2.tiles.annotation.TilesDefinition;
+import org.apache.struts2.tiles.annotation.TilesPutAttribute;
+
+import com.opensymphony.xwork2.ActionSupport;
+
+@Namespace("/tiles")
+@ParentPackage("tiles")
+@Result(name = "success", type="tiles")
+@TilesDefinition(extend = "showcase.annotations", putAttributes = {
+ @TilesPutAttribute(name = "header", value = "/WEB-INF/tiles/header.jsp"),
+ @TilesPutAttribute(name = "body", value = "/WEB-INF/tiles/body.ftl")
+})
+public class TilesAnnotationsAction extends ActionSupport {
+
+ private static final long serialVersionUID = 2900509995064928866L;
+
+}
diff --git a/apps/showcase/src/main/webapp/WEB-INF/tiles.xml b/apps/showcase/src/main/webapp/WEB-INF/tiles.xml
index 7c7057f67..a74de1b6b 100644
--- a/apps/showcase/src/main/webapp/WEB-INF/tiles.xml
+++ b/apps/showcase/src/main/webapp/WEB-INF/tiles.xml
@@ -22,8 +22,8 @@
-->
+ "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
+ "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
Notice that this is a layout made in JSP
+It is configured with annotations!
+ + diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessor.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessor.java new file mode 100644 index 000000000..fa5f7352f --- /dev/null +++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessor.java @@ -0,0 +1,177 @@ +/* + * 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.commons.lang3.StringUtils; +import org.apache.struts2.tiles.annotation.TilesAddAttribute; +import org.apache.struts2.tiles.annotation.TilesAddListAttribute; +import org.apache.struts2.tiles.annotation.TilesDefinition; +import org.apache.struts2.tiles.annotation.TilesDefinitions; +import org.apache.struts2.tiles.annotation.TilesPutAttribute; +import org.apache.struts2.tiles.annotation.TilesPutListAttribute; +import org.apache.tiles.Attribute; +import org.apache.tiles.Definition; +import org.apache.tiles.Expression; +import org.apache.tiles.ListAttribute; + +/** + * Processes tiles annotations to create {@link Definition}s and + * {@link Attribute}s in a way as close totiles.xml as possible.
+ *
+ */
+public class StrutsTilesAnnotationProcessor {
+
+ /**
+ * Search strategy is as follows:
+ * <add-attribute> element in tiles.xml.
+ *
+ */
+public @interface TilesAddAttribute {
+ String expression() default "";
+ String role() default "";
+ String type() default "";
+ String value() default "";
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesAddListAttribute.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesAddListAttribute.java
new file mode 100644
index 000000000..d65066fcc
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesAddListAttribute.java
@@ -0,0 +1,28 @@
+/*
+ * 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.annotation;
+
+/**
+ * Represents a <add-list-attribute> element in tiles.xml.
+ *
+ */
+public @interface TilesAddListAttribute {
+ String role() default "";
+ TilesAddAttribute[] addAttributes() default {};
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesDefinition.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesDefinition.java
new file mode 100644
index 000000000..3aa144c68
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesDefinition.java
@@ -0,0 +1,66 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Represents a <definition> element in tiles.xml.
+ *
+ *
+ * With a sample layout in tiles.xml like this:
+ *
+ * <definition name="layout" template="/WEB-INF/tiles/layout.jsp"> + * <put-attribute name="header" value=".header"/> + * <put-attribute name="body" value=".body"/> + * </definition> + *+ * + *
+ * You can annotate an action like that: + *
+ * @Result(name = "success", type="tiles")
+ * @TilesDefinition(extend = "layout", putAttributes = {
+ * @TilesPutAttribute(name = "header", value = "/WEB-INF/tiles/header.jsp"),
+ * @TilesPutAttribute(name = "body", value = "/WEB-INF/tiles/body.ftl")
+ * })
+ * public class FooAction extends ActionSupport {
+ *
+ *
+ *
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target(value = { ElementType.TYPE })
+@Inherited
+public @interface TilesDefinition {
+
+ String extend() default "";
+ String name() default "";
+ String preparer() default "";
+ String role() default "";
+ String template() default "";
+ String templateExpression() default "";
+ String templateType() default "";
+ TilesPutAttribute[] putAttributes() default {};
+ TilesPutListAttribute[] putListAttributes() default {};
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesDefinitions.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesDefinitions.java
new file mode 100644
index 000000000..fb5cc95bb
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesDefinitions.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An array of {@link TilesDefinition}s.
+ *
+ */
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target(value = { ElementType.TYPE })
+@Inherited
+public @interface TilesDefinitions {
+ TilesDefinition[] value();
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesPutAttribute.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesPutAttribute.java
new file mode 100644
index 000000000..c31d49ee7
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesPutAttribute.java
@@ -0,0 +1,32 @@
+/*
+ * 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.annotation;
+
+/**
+ * Represents a <put-attribute> element in tiles.xml.
+ *
+ */
+public @interface TilesPutAttribute {
+ boolean cascade() default false;
+ String expression() default "";
+ String name() default "";
+ String role() default "";
+ String type() default "";
+ String value() default "";
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesPutListAttribute.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesPutListAttribute.java
new file mode 100644
index 000000000..be7a507f2
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesPutListAttribute.java
@@ -0,0 +1,32 @@
+/*
+ * 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.annotation;
+
+/**
+ * Represents a <put-list-attribute> element in tiles.xml.
+ *
+ */
+public @interface TilesPutListAttribute {
+ boolean cascade() default false;
+ boolean inherit() default false;
+ String name() default "";
+ String role() default "";
+ TilesAddAttribute[] addAttributes() default {};
+ TilesAddListAttribute[] addListAttributes() default {};
+}
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 a4028690a..ecbdceee3 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
@@ -25,12 +25,20 @@ import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.result.ServletDispatcherResult;
+import org.apache.struts2.tiles.StrutsTilesAnnotationProcessor;
+import org.apache.struts2.tiles.annotation.TilesDefinition;
+import org.apache.tiles.Definition;
import org.apache.tiles.TilesContainer;
+import org.apache.tiles.TilesException;
import com.opensymphony.xwork2.ActionInvocation;
import org.apache.tiles.access.TilesAccess;
+import org.apache.tiles.mgmt.MutableTilesContainer;
import org.apache.tiles.request.ApplicationContext;
import org.apache.tiles.request.Request;
import org.apache.tiles.request.servlet.ServletRequest;
@@ -42,18 +50,11 @@ import org.apache.tiles.request.servlet.ServletUtil;
*
*
*
- * In your web.xml file, you need to add a servlet entry for TilesServlet to load the tiles
- * definitions into the ServletContext.
+ * In your web.xml file, you need to add a TilesListener.
*
- * <servlet>
- * <servlet-name>tiles</servlet-name>
- * <servlet-class>org.apache.tiles.servlets.TilesServlet</servlet-class>
- * <init-param>
- * <param-name>definitions-config</param-name>
- * <param-value>/WEB-INF/tiles-config.xml</param-value>
- * </init-param>
- * <load-on-startup>1</load-on-startup>
- * </servlet>
+ * <listener>
+ * <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
+ * </listener>
*
*
*
@@ -76,11 +77,20 @@ import org.apache.tiles.request.servlet.ServletUtil;
* </result-types>
*
*
+ *
+ *
+ * You have to configure tiles itself. Therefore you can add tiles.xml either
+ * to resources or WEB-INF. You may also use annotations like {@link TilesDefinition}.
+ *
+ *
+ *
*/
public class TilesResult extends ServletDispatcherResult {
private static final long serialVersionUID = -3806939435493086244L;
+ private static final Logger LOG = LogManager.getLogger(TilesResult.class);
+
public TilesResult() {
super();
}
@@ -99,6 +109,18 @@ public class TilesResult extends ServletDispatcherResult {
* HTTP request.
*/
public void doExecute(String location, ActionInvocation invocation) throws Exception {
+ StrutsTilesAnnotationProcessor annotationProcessor = new StrutsTilesAnnotationProcessor();
+ TilesDefinition tilesDefinition = null;
+ Object action = invocation.getAction();
+ String actionName = invocation.getInvocationContext().getName();
+
+ if (StringUtils.isEmpty(location)) {
+ LOG.trace("location not set -> action must have one @TilesDefinition");
+ tilesDefinition = annotationProcessor.findAnnotation(action, null);
+ String tileName = StringUtils.isNotEmpty(tilesDefinition.name()) ? tilesDefinition.name() : actionName;
+ location = tileName;
+ LOG.debug("using new location name '{}' and @TilesDefinition '{}'", location, tilesDefinition);
+ }
setLocation(location);
ServletContext servletContext = ServletActionContext.getServletContext();
@@ -111,6 +133,31 @@ public class TilesResult extends ServletDispatcherResult {
Request request = new ServletRequest(applicationContext, httpRequest, httpResponse);
+ boolean definitionValid = false;
+ try {
+ LOG.debug("checking if tiles definition exists '{}'", location);
+ definitionValid = container.isValidDefinition(location, request);
+ } catch (TilesException e) {
+ LOG.warn("got TilesException while checking if definiton exists, ignoring it", e);
+ }
+ if (!definitionValid) {
+ if (tilesDefinition == null) {
+ LOG.trace("tilesDefinition not found yet, searching in action");
+ tilesDefinition = annotationProcessor.findAnnotation(action, location);
+ }
+ if (tilesDefinition != null) {
+ Definition definition = annotationProcessor.buildTilesDefinition(location, tilesDefinition);
+ if (container instanceof MutableTilesContainer) {
+ LOG.debug("registering tiles definition with name '{}'", definition.getName());
+ ((MutableTilesContainer)container).register(definition, request);
+ } else {
+ LOG.error("cannot register tiles definition as tiles container is not mutable!");
+ }
+ } else {
+ LOG.warn("could not find @TilesDefinition for action: {}", actionName);
+ }
+ }
+
container.render(location, request);
}
}
diff --git a/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessorTest.java b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessorTest.java
new file mode 100644
index 000000000..c71d10092
--- /dev/null
+++ b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessorTest.java
@@ -0,0 +1,147 @@
+package org.apache.struts2.tiles;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.struts2.tiles.annotation.TilesDefinition;
+import org.apache.tiles.Attribute;
+import org.apache.tiles.Definition;
+import org.apache.tiles.Expression;
+import org.junit.Test;
+
+import org.junit.Assert;
+
+public class StrutsTilesAnnotationProcessorTest {
+
+ @Test
+ public void findAnnotationSingleAction() {
+ StrutsTilesAnnotationProcessor annotationProcessor = new StrutsTilesAnnotationProcessor();
+ TilesDefinition tilesDefinition = annotationProcessor.findAnnotation(new TilesTestActionSingleAnnotation(), null);
+ Assert.assertNotNull(tilesDefinition);
+ Assert.assertEquals("definition-name", tilesDefinition.name());
+ }
+
+ @Test
+ public void findAnnotationMultipleActionNameNull() {
+ StrutsTilesAnnotationProcessor annotationProcessor = new StrutsTilesAnnotationProcessor();
+ TilesDefinition tilesDefinition = annotationProcessor.findAnnotation(new TilesTestActionMultipleAnnotations(), null);
+ Assert.assertNotNull(tilesDefinition);
+ Assert.assertEquals("def1", tilesDefinition.name());
+ }
+
+ @Test
+ public void findAnnotationMultipleActionNameGiven() {
+ StrutsTilesAnnotationProcessor annotationProcessor = new StrutsTilesAnnotationProcessor();
+ TilesDefinition tilesDefinition = annotationProcessor.findAnnotation(new TilesTestActionMultipleAnnotations(), "def2");
+ Assert.assertNotNull(tilesDefinition);
+ Assert.assertEquals("def2", tilesDefinition.name());
+ }
+
+ @Test
+ public void findAnnotationMultipleActionNotFound() {
+ StrutsTilesAnnotationProcessor annotationProcessor = new StrutsTilesAnnotationProcessor();
+ TilesDefinition tilesDefinition = annotationProcessor.findAnnotation(new TilesTestActionMultipleAnnotations(), "def3");
+ Assert.assertNull(tilesDefinition);
+ }
+
+ @Test
+ public void buildDefiniton() {
+ StrutsTilesAnnotationProcessor annotationProcessor = new StrutsTilesAnnotationProcessor();
+ TilesDefinition tilesDefinition = annotationProcessor.findAnnotation(new TilesTestActionSingleAnnotation(), null);
+
+ Definition definition = annotationProcessor.buildTilesDefinition("tileName", tilesDefinition);
+
+ Assert.assertNotNull(definition);
+ Assert.assertEquals("tileName", definition.getName());
+ Assert.assertEquals("preparer", definition.getPreparer());
+ Assert.assertEquals("base-definition", definition.getExtends());
+ Attribute templateAttribute = definition.getTemplateAttribute();
+ Assert.assertEquals("template", templateAttribute.getValue());
+ Assert.assertEquals("type", templateAttribute.getRenderer());
+ Assert.assertEquals("role", templateAttribute.getRole());
+ Expression definitionExpressionObject = templateAttribute.getExpressionObject();
+ Assert.assertEquals("templ*", definitionExpressionObject.getExpression());
+ Assert.assertNull(definitionExpressionObject.getLanguage());
+
+ Attribute putAttribute = definition.getAttribute("put-attr");
+ Assert.assertNotNull(putAttribute);
+ Assert.assertEquals("attr-val", putAttribute.getValue());
+ Assert.assertEquals("attr-type", putAttribute.getRenderer());
+ Assert.assertEquals("attr-role", putAttribute.getRole());
+ Expression putAttrExpressionObject = putAttribute.getExpressionObject();
+ Assert.assertEquals("expr", putAttrExpressionObject.getExpression());
+ Assert.assertEquals("lang", putAttrExpressionObject.getLanguage());
+
+ Attribute listAttribute = definition.getAttribute("list-name");
+ Assert.assertEquals("list-role", listAttribute.getRole());
+ List