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"> @@ -45,4 +45,10 @@ + + + + + + diff --git a/apps/showcase/src/main/webapp/WEB-INF/tiles/body.jsp b/apps/showcase/src/main/webapp/WEB-INF/tiles/body.jsp index e2e35126f..855a89688 100644 --- a/apps/showcase/src/main/webapp/WEB-INF/tiles/body.jsp +++ b/apps/showcase/src/main/webapp/WEB-INF/tiles/body.jsp @@ -16,6 +16,9 @@
  • View Example with a FreeMarker Layout
  • +
  • + View Example with tiles configuration by annotating action +
  • diff --git a/apps/showcase/src/main/webapp/WEB-INF/tiles/layout-annotations.jsp b/apps/showcase/src/main/webapp/WEB-INF/tiles/layout-annotations.jsp new file mode 100644 index 000000000..5609cb1a1 --- /dev/null +++ b/apps/showcase/src/main/webapp/WEB-INF/tiles/layout-annotations.jsp @@ -0,0 +1,14 @@ +<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> +<%@ taglib prefix="s" uri="/struts-tags" %> + +<%-- Show usage; Used in Header --%> + + + Struts2 Showcase - <tiles:getAsString name="title"/> + + + +

    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 to tiles.xml as possible. + * + */ +public class StrutsTilesAnnotationProcessor { + + /** + * Search strategy is as follows: + *
      + *
    • Check if action has Annotation {@link TilesDefinition}
    • + *
    • If not, check if action has Annotation {@link TilesDefinitions}
    • + *
    • If given tileName is not null and present in {@link TilesDefinitions}, return it
    • + *
    • Return first element of {@link TilesDefinitions}
    • + *
    • Return null
    • + *
    + * + * @param action + * Annotated action. + * @param tileName + * Tilename to search for. May be null in some circumstances. + * @return {@link TilesDefinition} + */ + public TilesDefinition findAnnotation(Object action, String tileName) { + Class clazz = action.getClass(); + TilesDefinition tilesDefinition = clazz.getAnnotation(TilesDefinition.class); + TilesDefinitions tilesDefinitions = clazz.getAnnotation(TilesDefinitions.class); + + if (tilesDefinition == null && tilesDefinitions != null) { + if (!StringUtils.isEmpty(tileName)) { + for (TilesDefinition i : tilesDefinitions.value()) { + if (i.name() != null && i.name().equals(tileName)) { + tilesDefinition = i; + break; + } + } + } else { + if (tilesDefinitions.value().length > 0) { + tilesDefinition = tilesDefinitions.value()[0]; + } + } + } + + return tilesDefinition; + } + + /** + * Builds a {@link Definition} based on given {@link TilesDefinition} with + * given name. + * + * @param tileName + * name for resulting {@link Definition}. + * @param tilesDefinition + * {@link TilesDefinition} to process. + * @return {@link Definition} represented by given {@link TilesDefinition}. + */ + public Definition buildTilesDefinition(String tileName, TilesDefinition tilesDefinition) { + Definition definition = new Definition(); + + definition.setName(tileName); + + String extend = getValueOrNull(tilesDefinition.extend()); + if (extend != null) { + definition.setExtends(extend); + } + String preparer = getValueOrNull(tilesDefinition.preparer()); + if (preparer != null) { + definition.setPreparer(preparer); + } + definition.setTemplateAttribute(buildTemplateAttribute(tilesDefinition)); + + for (TilesPutAttribute putAttribute : tilesDefinition.putAttributes()) { + Attribute attribute = buildPutAttribute(putAttribute); + definition.putAttribute(putAttribute.name(), attribute, putAttribute.cascade()); + } + for (TilesPutListAttribute putListAttribute : tilesDefinition.putListAttributes()) { + Attribute attribute = buildPutListAttribute(putListAttribute); + definition.putAttribute(putListAttribute.name(), attribute, putListAttribute.cascade()); + } + + return definition; + } + + protected Attribute buildTemplateAttribute(TilesDefinition tilesDef) { + // see tiles DigesterDefinitionsReader + Attribute attribute = Attribute.createTemplateAttribute(getValueOrNull(tilesDef.template())); + String templateExpression = getValueOrNull(tilesDef.templateExpression()); + Expression expression = Expression.createExpressionFromDescribedExpression(templateExpression); + attribute.setExpressionObject(expression); + attribute.setRole(getValueOrNull(tilesDef.role())); + String templateType = getValueOrNull(tilesDef.templateType()); + if (templateType != null) { + attribute.setRenderer(templateType); + } else if (getValueOrNull(tilesDef.extend()) != null && templateType == null) { + attribute.setRenderer(null); + } + return attribute; + } + + protected Attribute buildPutAttribute(TilesPutAttribute putAttribute) { + Attribute attribute = new Attribute(); + attribute.setValue(getValueOrNull(putAttribute.value())); + String expression = getValueOrNull(putAttribute.expression()); + attribute.setExpressionObject(Expression.createExpressionFromDescribedExpression(expression)); + attribute.setRole(getValueOrNull(putAttribute.role())); + attribute.setRenderer(getValueOrNull(putAttribute.type())); + return attribute; + } + + protected Attribute buildPutListAttribute(TilesPutListAttribute putListAttribute) { + ListAttribute attribute = new ListAttribute(); + attribute.setRole(getValueOrNull(putListAttribute.role())); + attribute.setInherit(putListAttribute.inherit()); + for (TilesAddAttribute addAttribute : putListAttribute.addAttributes()) { + attribute.add(buildAddAttribute(addAttribute)); + } + for (TilesAddListAttribute addListAttribute : putListAttribute.addListAttributes()) { + attribute.add(buildAddListAttribute(addListAttribute)); + } + return attribute; + } + + protected Attribute buildAddAttribute(TilesAddAttribute addAttribute) { + Attribute attribute = new Attribute(); + attribute.setValue(getValueOrNull(addAttribute.value())); + String expression = getValueOrNull(addAttribute.expression()); + attribute.setExpressionObject(Expression.createExpressionFromDescribedExpression(expression)); + attribute.setRole(getValueOrNull(addAttribute.role())); + attribute.setRenderer(getValueOrNull(addAttribute.type())); + return attribute; + } + + protected Attribute buildAddListAttribute(TilesAddListAttribute addListAttribute) { + ListAttribute attribute = new ListAttribute(); + attribute.setRole(getValueOrNull(addListAttribute.role())); + for (TilesAddAttribute addAttribute : addListAttribute.addAttributes()) { + attribute.add(buildAddAttribute(addAttribute)); + } + return attribute; + } + + protected String getValueOrNull(String value) { + return value != null && value.length() > 0 ? value : null; + } +} diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesAddAttribute.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesAddAttribute.java new file mode 100644 index 000000000..0e668de0d --- /dev/null +++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/annotation/TilesAddAttribute.java @@ -0,0 +1,30 @@ +/* + * 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-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 listValue = getListValue(listAttribute); + Assert.assertEquals(2, listValue.size()); + + Attribute addAttribute = listValue.get(0); + Assert.assertEquals("list-attr-role", addAttribute.getRole()); + Assert.assertEquals("list-attr-val", addAttribute.getValue()); + Assert.assertEquals("list-attr-type", addAttribute.getRenderer()); + Expression addAttrExpressionObject = addAttribute.getExpressionObject(); + Assert.assertEquals("list-attr-expr", addAttrExpressionObject.getExpression()); + + Attribute addListAttribute = listValue.get(1); + Assert.assertEquals("list-list-attr-role", addListAttribute.getRole()); + List addListValue = getListValue(addListAttribute); + Assert.assertEquals(1, addListValue.size()); + Assert.assertEquals("list-list-add-attr", addListValue.get(0).getValue()); + + Set cascadedAttributeNames = definition.getCascadedAttributeNames(); + Assert.assertEquals(2, cascadedAttributeNames.size()); + Assert.assertTrue(cascadedAttributeNames.contains("put-attr")); + Assert.assertTrue(cascadedAttributeNames.contains("list-name")); + } + + @Test + public void buildDefinitonAllEmpty() { + StrutsTilesAnnotationProcessor annotationProcessor = new StrutsTilesAnnotationProcessor(); + TilesDefinition tilesDefinition = annotationProcessor.findAnnotation(new TilesTestActionSingleAnnotationAllEmpty(), null); + + Definition definition = annotationProcessor.buildTilesDefinition(null, tilesDefinition); + + Assert.assertNotNull(definition); + Assert.assertNull(definition.getName()); + Assert.assertNull(definition.getPreparer()); + Assert.assertNull(definition.getExtends()); + Attribute templateAttribute = definition.getTemplateAttribute(); + Assert.assertNull(templateAttribute.getValue()); + Assert.assertNull(templateAttribute.getRole()); + Assert.assertNull(templateAttribute.getExpressionObject()); + + Attribute putAttribute = definition.getAttribute("put-attr"); + Assert.assertNotNull(putAttribute); + Assert.assertNull(putAttribute.getValue()); + Assert.assertNull(putAttribute.getRenderer()); + Assert.assertNull(putAttribute.getRole()); + Assert.assertNull(putAttribute.getExpressionObject()); + + Attribute listAttribute = definition.getAttribute("list-name"); + Assert.assertNull(listAttribute.getRole()); + List listValue = getListValue(listAttribute); + Assert.assertEquals(2, listValue.size()); + + Attribute addAttribute = listValue.get(0); + Assert.assertNull(addAttribute.getRole()); + Assert.assertNull(addAttribute.getValue()); + Assert.assertNull(addAttribute.getRenderer()); + Assert.assertNull(addAttribute.getExpressionObject()); + + Attribute addListAttribute = listValue.get(1); + Assert.assertNull(addListAttribute.getRole()); + List addListValue = getListValue(addListAttribute); + Assert.assertEquals(1, addListValue.size()); + Assert.assertNull(addListValue.get(0).getValue()); + + Set cascadedAttributeNames = definition.getCascadedAttributeNames(); + Assert.assertNull(cascadedAttributeNames); + } + + @SuppressWarnings("unchecked") + protected List getListValue(Attribute listAttribute) { + return (List) listAttribute.getValue(); + } +} diff --git a/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesTestActionMultipleAnnotations.java b/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesTestActionMultipleAnnotations.java new file mode 100644 index 000000000..99a736bb6 --- /dev/null +++ b/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesTestActionMultipleAnnotations.java @@ -0,0 +1,12 @@ +package org.apache.struts2.tiles; + +import org.apache.struts2.tiles.annotation.TilesDefinition; +import org.apache.struts2.tiles.annotation.TilesDefinitions; + +@TilesDefinitions({ + @TilesDefinition(name="def1"), + @TilesDefinition(name="def2"), +}) +public class TilesTestActionMultipleAnnotations { + +} diff --git a/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesTestActionSingleAnnotation.java b/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesTestActionSingleAnnotation.java new file mode 100644 index 000000000..c7a98ebff --- /dev/null +++ b/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesTestActionSingleAnnotation.java @@ -0,0 +1,49 @@ +package org.apache.struts2.tiles; + +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.TilesPutAttribute; +import org.apache.struts2.tiles.annotation.TilesPutListAttribute; + +@TilesDefinition( + name = "definition-name", + extend = "base-definition", + preparer = "preparer", + role = "role", + template = "template", + templateExpression = "templ*", + templateType = "type", + putAttributes = { + @TilesPutAttribute( + cascade = true, + expression = "lang:expr", + name = "put-attr", + role = "attr-role", + type = "attr-type", + value = "attr-val") + }, + putListAttributes = { + @TilesPutListAttribute( + cascade = true, + inherit = true, + name = "list-name", + role = "list-role", + addAttributes = { + @TilesAddAttribute( + expression = "list-attr-expr", + role = "list-attr-role", + type = "list-attr-type", + value = "list-attr-val") + }, + addListAttributes = { + @TilesAddListAttribute( + role = "list-list-attr-role", + addAttributes = {@TilesAddAttribute("list-list-add-attr")}) + } + ) + } +) +public class TilesTestActionSingleAnnotation { + +} diff --git a/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesTestActionSingleAnnotationAllEmpty.java b/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesTestActionSingleAnnotationAllEmpty.java new file mode 100644 index 000000000..4a9aff9d3 --- /dev/null +++ b/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesTestActionSingleAnnotationAllEmpty.java @@ -0,0 +1,28 @@ +package org.apache.struts2.tiles; + +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.TilesPutAttribute; +import org.apache.struts2.tiles.annotation.TilesPutListAttribute; + +@TilesDefinition( + putAttributes = { + @TilesPutAttribute(name = "put-attr") + }, + putListAttributes = { + @TilesPutListAttribute( + name = "list-name", + addAttributes = { + @TilesAddAttribute() + }, + addListAttributes = { + @TilesAddListAttribute( + addAttributes = {@TilesAddAttribute()}) + } + ) + } +) +public class TilesTestActionSingleAnnotationAllEmpty { + +}