Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

Name

Convention Plugin

Publisher

Brian Pontarelli Apache Software Foundation

License

Open Source (ASL2)

Version

Bundled with Struts from 2.1 .1-SNAPSHOT

Homepage

You're at the homepage

on

Homepage

http://cwiki.apache.org/confluence/display/WW/Convention+Plugin

Download

Part of the Struts2 distribution

Wiki Markup
{rate:title=Rating|theme=dynamic}

...

The Convention Plugin should require no configuration to use. Many of the conventions can be controlled using configuration properties and many of the classes can be extended or overridden.

Availability

The Convention plugin is not part of any Struts release yet. It is located on the Struts 2 svn sandbox

Setup

In order to use the Convention plugin, you first need to add the JAR file to the WEB-INF/lib directory of your application.

Hello world

Now that the Convention plugin has been added to your application, let's start with a very simple example. This example will use an actionless result that is identified by the URL. By default, the Convention plugin assumes that all of the results are stored in WEB-INF/content. This can be changed by setting the property struts.convention.result.path in the Struts properties file to the new location. Don't worry about trailing slashes, the Convention plugin handles this for you. Here is our hello world JSP:

Code Block
titleWEB-INF/content/hello-world.jsp
borderStylesolid

<html>
<body>
Hello world!
</body>
</html>

If you start Tomcat (or whichever J2EE container you are using) and type in http://localhost:8080/hello-worldImage Removed into your browser you should get this result:

Code Block
titleWEB-INF/content/hello-world.jsp
borderStylesolid

Hello world!

This illustrates that the Convention plugin will find results even when no action exists and it is all based on the URL passed to Struts.

Code behind hello world

Let's expand on this example and add a code behind class. In order to do this we need to ensure that the Convention plugin is able to find our action classes. By default, the Convention plugin will find all action classes that implement com.opensymphony.xwork2.Action or whose name ends with the word Action in specific packages.

These packages are located by the Convention plugin using a search methodology. First the Convention plugin finds packages named struts, struts2, action or actions. Any packages that match those names are considered the root packages for the Convention plugin. Next, the plugin looks at all of the classes in those packages as well as sub-packages and determines if the classes implement com.opensymphony.xwork2.Action or if their name ends with Action (i.e. FooAction). Here's an example of a few classes that the Convention plugin will find:

Code Block
titleClasses
borderStylesolid

com.example.actions.MainAction
com.example.actions.products.Display (implements com.opensymphony.xwork2.Action)
com.example.struts.company.details.ShowCompanyDetailsAction

Each of the action classes that the plugin finds will be configured to respond to specific URLs. The URL is based on the package name that the class is defined in and the class name itself. First the plugin determines the namespace of the URL using the package names between the root package and the package the class is defined in. For our examples above, the namespaces would be:

Code Block
titleNamespaces
borderStylesolid

com.example.actions.MainAction -> /
com.example.actions.products.Display -> /products
com.example.struts.company.details.ShowCompanyDetailsAction -> /company/details

Next, the plugin determines the URL of the resource using the class name. It first removes the word Action from the end of the class name and then converts camel case names to dashes. In our example the full URLs would be:

Code Block
titleFull URLs
borderStylesolid

com.example.actions.MainAction -> /main
com.example.actions.products.Display -> /products/display
com.example.struts.company.details.ShowCompanyDetailsAction -> /company/details/show-company-details

You can tell the Convention plugin to ignore certain packages using the property struts.convention.exclude.packages. You can also tell the plugin to use different strings to locate root packages using the property struts.convention.package.locators. Finally, you can tell the plugin to search specific root packages using the property struts.convention.action.packages.

Here is our code behind action class:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 

public class HelloWorld extends ActionSupport {
  private String message;

  public String getMessage() {
    return message;
  }

  public String execute() {
    message = "Hello World!";
    return SUCCESS;
  }
}

If you compile this class and place it into your application in the WEB-INF/classes, the Convention plugin will find the class and map the URL /hello-world to it. Next, we need to update our JSP to print out the message we setup in the action class. Here is the new JSP:

Code Block
titleWEB-INF/content/hello-world.jsp
borderStylesolid

<html>
<body>
The message is ${message}
</body>
</html>

If start up the application server and open up http://localhost:8080/hello-worldImage Removed in our browser, we should get this result:

Code Block
titleResult
borderStylesolid

The message is Hello World!

Results and result codes

The Convention Plugin will pre-configure all of you action classes when Struts is started. By default, this configuration will also contain results for any JSPs that it can find within the application. The JSPs have an additional feature that allows different JSPs to be used based on the result code of the action. Since action methods return Strings and these Strings are traditionally used to locate results for the action, the Convention plugin allows you to define different results based on the result code.

Building on our example from above, let's say we want to provide a different result if the result code from our action is the String zero rather than success. First, we update the action class to return different result codes:

...

titlecom.example.actions.HelloWorld
borderStylesolid

...

.

...

Next, we add a new JSP to the application named WEB-INF/content/hello-world-zero.jsp. Notice that the first part of the file name is the same as the URL of the action and the last part of the name is the result code. This is the convention that the plugin uses to determine which results to render. Here is our new JSP:

Code Block
titleWEB-INF/content/hello-world.jsp
borderStylesolid

<html>
<body>
The error message is ${message}
</body>
</html>

Now, if you compile the action and restart the application, based on the current time, you'll either see the result from WEB-INF/content/hello-world.jsp or WEB-INF/content/hello-world-zero.jsp.

The result type is based on the extension of the file. The supported extensions are: jsp,ftl,vm,html,html. Examples of Action and Result to Template mapping:

URL

Result

File that could match

Result Type

/hello

success

/WEB-INF/content/hello.jsp

Dispatcher

/hello

success

/WEB-INF/content/hello-success.htm

Dispatcher

/hello

success

/WEB-INF/content/hello.ftl

FreeMarker

/hello-world

input

/WEB-INF/content/hello-world-input.vm

Velocity

/test1/test2/hello

error

/WEB-INF/content/test/test2/hello-error.html

Dispatcher

XWork packages

Actions are placed on a custom XWork package which prevents conflicts. The name of this package is based on the Java package the action is defined in, the namespace part of the URL for the action and the parent XWork package for the action. The parent XWork package is determined based on the property named struts.convention.default.parent.package(defaults to conventionDefault), which is a custom XWork package that extends strutsDefault.

Therefore the naming for XWork packages used by the Convention plugin are in the form:

Code Block
titleXWork package naming
borderStylesolid

<java-package>#<namespace>#<parent-package>

Using our example from above, the XWork package for our action would be:

Code Block
titleXWork package naming
borderStylesolid

com.example.actions#/#conventionDefault

Annotation reference

The Convention plugin uses a number of different annotations to override the default conventions that are used to map actions to URLs and locate results. In addition, you can modify the parent XWork package that actions are configured with.

Action(s) annotation

The Convention plugin allows action classes to change the URL that they are mapped to using the Action annotation. This annotation can also be used inside the Actions annotation to allow multiple URLs to map to a single action class. This annotation must be defined on action methods like this:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;

public class HelloWorld extends ActionSupport {
  @Action("/different/url")
  public String execute() {
    return SUCCESS;
  }
}

Our action class will now map to the URL /different/url rather than /hello-world. If no @Result (see next section) is specified, then the namespace of the action will be used as the path to the result, on our last example it would be "/WEB-INF/content/different/url.jsp".

A single method within an action class can also map to multiple URLs using the Actions annotation like this:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;

public class HelloWorld extends ActionSupport {
  @Actions({
    @Action("/different/url"),
    @Action("/another/url")
  })
  public String execute() {
    return SUCCESS;
  }
}

Another usage of the Action or Actions annotation is to define multiple action methods within a single action class, each of which respond to a different URL. Here is an example of multiple action methods:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;

public class HelloWorld extends ActionSupport {
  @Action("/different/url")
  public String execute() {
    return SUCCESS;
  }

  @Action("url")
  public String doSomething() {
    return SUCCESS;
  }
}

The previous example defines a second URL that is not fully qualified. This means that the namespace for the URL is determined using the Java package name rather than the Action annotation.

Interceptor and interceptor stacks can be specified using the interceptorRefs attribute. The following example applies the "validation" interceptor and the "defaultStack" interceptor stack to the action:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;

public class HelloWorld extends ActionSupport {
  @Action(interceptorRefs={@InterceptorRef("validation"), @InterceptorRef("defaultStack")})
  public String execute() {
    return SUCCESS;
  }

  @Action("url")
  public String doSomething() {
    return SUCCESS;
  }
}

Parameters can be passed to results using the params attribute. The value of this attribute is a string array with an even number of elements in the form {"key0", "value0, "key1", "value1" ... "keyN", "valueN"}. For example:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;

public class HelloWorld extends ActionSupport {
  @Action(interceptorRefs=@InterceptorRef(value="validation",params={"programmatic", "false", "declarative", "true}))
  public String execute() {
    return SUCCESS;
  }

  @Action("url")
  public String doSomething() {
    return SUCCESS;
  }
}

If interceptors are not specified, the default stack is applied.

InterceptorRef(s) annotation

Interceptors can be specified at the method level, using the Action annotation or at the class level using the InterceptorRefs annotation. Interceptors specified at the class level will be applied to all actions defined on that class. In the following example:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;

@InterceptorRefs({
    @InterceptorRef("interceptor-1"),
    @InterceptorRef("defaultStack")
})
public class HelloWorld extends ActionSupport {
  @Action(value="action1", interceptorRefs=@InterceptorRef("validation"))
  public String execute() {
    return SUCCESS;
  }

  @Action(value="action2")
  public String doSomething() {
    return SUCCESS;
  }
}

The following interceptors will be applied to "action1": "interceptor-1", all interceptors from "defaultStack", "validation".
All interceptors from "defaultStack" will be applied to "action2".

Result(s) annotation

The Convention plugin allows action classes to define different results for an action. Results fall into two categories, global and local. Global results are shared across all actions defined within the action class. These results are defined as annotations on the action class. Local results apply only to the action method they are defined on. Here is an example of the different types of result annotations:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;

@Results({
  @Result(name="failure", location="fail.jsp")
})
public class HelloWorld extends ActionSupport {
  @Action(value="/different/url", 
    results={@Result(name="success", location="http://struts.apache.org", type="redirect")}
  )
  public String execute() {
    return SUCCESS;
  }

  @Action("/another/url")
  public String doSomething() {
    return SUCCESS;
  }
}

Parameters can be passed to results using the params attribute. The value of this attribute is a string array with an even number of elements in the form {"key0", "value0, "key1", "value1" ... "keyN", "valueN"}. For example:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;

public class HelloWorld extends ActionSupport {
  @Action(value="/different/url", 
    results={@Result(name="success", type="httpheader", params={"status", "500", "errorMessage", "Internal Error"})}
  )
  public String execute() {
    return SUCCESS;
  }

  @Action("/another/url")
  public String doSomething() {
    return SUCCESS;
  }
}

Namespace annotation

The namespace annotation allows the namespace for action classes to be changed instead of using the convention of the Java package name. This annotation can be placed on an action class or within the package-info.java class that allows annotations to be placed on Java packages. When this annotation is put on an action class, it applies to all actions defined in the class, that are not fully qualified action URLs. When this annotation is place in the package-info.java file, it changes the default namespace for all actions defined in the Java package. Here is an example of the annotation on an action class:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;

@Namespace("/custom")
public class HelloWorld extends ActionSupport {
  @Action("/different/url")
  public String execute() {
    return SUCCESS;
  }

  @Action("url")
  public String doSomething() {
    return SUCCESS;
  }
}

In this example, the action will respond to two different URLs /different/url and /custom/url.

Here is an example of using this annotation in the package-info.java file:

Code Block
titlecom/example/actions/package-info.java
borderStylesolid

@org.apache.struts2.convention.annotation.Namespace("/custom")
package com.example.actions;

This changes the default namespace for all actions defined in the package com.example.actions. This annotation however doesn't apply to sub-packages.

ResultPath annotation

The ResultPath annotation allows applications to change the location where results are stored. This annotation can be placed on an action class and also in the package-info.java file. Here is an example of using this annotation:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.ResultPath;

@ResultPath("/WEB-INF/jsps")
public class HelloWorld extends ActionSupport {
  public String execute() {
    return SUCCESS;
  }
}

The result for this class will be located in WEB-INF/jsps rather than the default of WEB-INF/content.

ParentPackage annotation

The ParentPackage annotation allows applications to define different parent XWork packages for specific action classes or Java packages. Here is an example of using the annotation on an action class:

Code Block
titlecom.example.actions.HelloWorld
borderStylesolid

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport; 
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.ParentPackage;

@ParentPackage("customXWorkPackage")
public class HelloWorld extends ActionSupport {
  public String execute() {
    return SUCCESS;
  }
}

Configuration reference

Add a constant element to your struts config file to change the value of a configuration setting, like:

...


<constant name="struts.convention.result.path" value="/WEB-INF/mytemplates/"/>

Name

Default Value

Description

struts.convention.action.disableJarScanning

true

Scan jar files for actions

struts.convention.action.packages

 

An optional list of action packages that this should create configuration for (they don't need to match a locator pattern)

struts.convention.result.path

/WEB-INF/content/

Directory where templates are located

struts.convention.result.flatLayout

true

If set to false, the result can be put in its own directory: resultsRoot/namespace/actionName/result.extension

...

struts.convention.action.suffix

...

Action

...

Suffix used to find actions based on class names

...

struts.convention.action.disableScanning

...

false

...

Scan packages for actions

...

struts.convention.action.mapAllMatches

...

false

...

Create action mappings, even if no @Action is found

...

struts.convention.action.checkImplementsAction

...

true

...

Check if an action implements com.opensymphony.xwork2.Action to create an action mapping

...

struts.convention.default.parent.package

...

convention-default

...

Default parent package for action mappins

...

struts.convention.action.name.lowercase

...

true

...

Convert action name to lowercase

...

struts.convention.action.name.separator

...

-

...

Separator used to build the action name, MyAction -> my-action

...

struts.convention.package.locators

...

action,actions,struts,struts2

...

Packages whose name end with one of these strings will be scanned for actions

...

struts.convention.package.locators.disable

...

false

...

Disable the scanning of packages based on package locators

...

struts.convention.exclude.packages

...

org.apache.struts.*,
org.apache.struts2.*,
org.springframework.web.struts.*,
org.springframework.web.struts2.*,
org.hibernate.*

...

Packages excluded from the action scanning

...

struts.convention.relative.result.types

...

dispatcher,velocity,freemarker

...

The list of result types that can have locations that are relative and the result location (which is the resultPath plus the namespace) prepended to them

...

struts.convention.redirect.to.slash

...

true

...