Struts: Frequently Asked Questions

Struts: Frequently Asked Questions

Answers to questions on Jive's use of Struts, an open framework for supporting a model-view-controller architecture.

Why does Jive use Struts 2.0?

Jive has a long history with the WebWork framework, which became Struts 2.0. In fact, Patrick Lightbody, the creator of WebWork is a former Jive employee. It's served us well as our MVC web framework.

Is it difficult to migrate from WebWork 2 to Struts 2?

No. Essentially, Struts 2.0 is the technical equivalent of WebWork 2.3. Aside from the package and property renaming, it isn't much different from, say, migrating from WebWork 2.1 to 2.2.

Key Changes from Webwork

Webwork Migration Strategies

What does the .jspa file extension stand for?

A .jspa web extension such as search.jspa?communityID=1 represents a servlet alias; therefore, .jspa files do not exist on the file system. Jive Forums and Jive Knowledge Base both use a web framework called Struts and the .jspa extension maps to the Struts servlet controller. So when a request is made to search.jspa?communityID=1, the following actions occur:

  1. The application server knows to pass control to the dispatcher servlet because of the  .jspa mapping declared in the web.xml file.
  2. The dispatcher servlet knows which action to execute based on the alias ( search in this case). An action is a Java bean which handles logic for the view. In this case, a forum is requested so the forum action is loaded and other things like permissions and authentication are handled.
  3. The action executes and returns a response code. Based on that code, the dispatcher servlet will redirect to a FTL template. The template is executed and uses objects loaded by the action class.

To determine what FTLs are executed when a .jspa is called, view the struts-actions.xml file. If you are using Jive SBS or Jive Forums 5.5 or later, the file is called xwork-community.xml and is in the JAR file.

How does Jive implement pretty URLs? When/how does Jive use the URLMapper? How can I customize this?

See struts.properties. There is a key called struts.mapper.class where we provide our own custom implementation of ActionMapper , which is how Struts figures out how to map a request to an action. The default is to map actions to the <action name> + .jspa; for example, when you map the LoginAction.java to the URL login you would access that via the web by login.jspa. We've extended that concept to support certain friendly URLs, such as /blogs/people/aaron. These URLs are implemented through a number of subclasses of URLMapping that manually associate an action with a request path.

See this blog post about Clearspace and Meaningful URLs

How do we use Struts type converters?

Starting in version 2.0, Jive implemented the type conversion Struts 2.0 facility to populate actions with the correct objects. For example if you're setting the community with a community ID for the URL /community/feeds/allcontent?community=2001, Struts would find the com.jivesoftware.community.web.struts.converter.CommunityConverter, look up the correct community object based on the ID, and set it in the action.


The mapping file is xwork-conversion.properties.

There is not currently a way to modify this from within a plugin.

How do we use Struts interceptors?

Jive uses interceoptors extensively to provide functionality across a large number of actions. Examples include the ReferrerInterceptor which maintains the previous URL in the session, and the ViewCountInterceptor which updates content read count numbers. This is good if you need to do some pre- or post-processing of the action event from the Struts layer.


Here is the Struts Interceptor documentation.

These aren't currently modifiable from a plugin.

Historically, Jive used an IOCInterceptor to achieve dependency injection and security-related interceptors; those no longer apply in version 2.0, as we use the Spring framework for dependency injection and Acegi for security (now invoked from the Servlet Filter layer).

Does Jive SBS use auto wiring in Struts? Do we use Spring with Struts?

Yes. In the struts.properties file we set this to use the Spring framework:

struts.objectFactory=spring

In other words, CS uses the Spring framework to inject Spring beans into our Struts actions. The actions are autowired by the name of the defined Spring bean:  struts.objectFactory.spring.autowire=name. If you're writing a plugin, this means that you can access any Spring bean from your action by writing a setter method using the JavaBean convention. For example, if you need the CommunityManager you simply add the method setCommunityManager  to your action. If you're writing your own Spring bean, you declare it in your plugin and follow the same convention.

public void setCommunityManager(CommunityManager communityManager) {
    this.communityManager = communityManager;
}

What is the struts-community*.xml file?

It contains all the struts action definitions.

The most important files are:

Other interesting files (not usually customized):

What are some best practices for writing forms?

  •   addActionError() --  This generally displays the message at the top of the page.
  •   addFieldError() -- This message should appear next to the input field.
  •   addActionMessage() -- This message will appear at the top of the page.

    I don't want my action to be wrapped by SiteMesh. How do I do that?

    The best way is to set the following annotation on your action class:  @Decorate(false)

    You can also modify the templates.xml file by adding your URL to the excludes section, if declaring it globally at the action level doesn't work for you. For example, you want to decorate an input form /form!input.jspa but not the result,  /form.jspa

    <excludes> 

        <pattern>/admin/system-log.js*</pattern>
        ...
    </excludes>

    How do I get the application to ignore my action when redirecting after a login?

    Set the following annotation on your Action class:  SetReferer(false)

    How do Struts and SiteMesh work together?

    SiteMesh actually wraps every servlet request from a servlet filter, buffers the output of every request passed in and out of Struts, wrapping the result in a header and footer. If you ever choose not to decorate your request, disable it using one of the methods described above.

    When/why would I use the ActionContext?

    You can access the ActionContext from your action class via the   public static ActionContext getContext() method. This object will allow you to access various lower-level objects such as the current Request, Session, ActionInvocation.

    How to do non FTL results?

    You can invoke one of the standard Struts or custom result types in your action declaration. For example:

    <action name="spellcheck" class="com.jivesoftware.community.action.SpellCheckAction"> 

         <result name="success" type="stream">
             <param name="parse">false</param>
             <param name="contentType">text/json</param>
             <param name="inputName">jsonStream</param>
             <param name="bufferSize">1024</param>
         </result>
    </action>

    When should we use sessions?

    Never, ideally. If you use sessions, you are forced to use sticky sessions on your load balancer when you deploy to a cluster.

    What are the *ActionSupport classes?

    We've grouped convenience methods for your actions under the following classes:

    What are the steps you go through when debugging a problem with Struts?

    If the request URL ends with .jspa, you can look up the action class using struts-community.xml.

    If the request URL doesn't end with .jspa, you'll need to find the URLMapping that maps the URL to the action name. The following URLMapper classes exist in version 2.5:

    The line that you'll be looking for will look something like this:

    mapping.setName("view-community-stats-feed");

    Given the name of the action from the URLMapping class, you can then find the action class from struts-community.xml and you should also be able to find the FreeMarker template associated with the action name.

    Now that you have the  action class, you can set a breakpoint in the action's execute method, etc.

    Any gotchas doing substitutions in your struts.xml?

    When referencing action properties in your struts-community-custom.xml (struts.xml for plugins) file, don't use any Freemarker built-ins (directives).  This can be tempting to do because the syntax is very similar, but Struts isn't actually using Freemarker substitution here, it's using pure OGNL syntax.  Freemarker is a super-set of OGNL and the full feature set won't work here.


    Wrong:

    <result name="success" type="redirect">my-new-action.jspa?docID=${document.ID?C}

    Notice the use of the freemarker directive ?C.  This will break.

    Correct:

    <result name="success" type="redirect">my-new-action.jspa?docID=${document.ID}</result>