Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Modularity With Apache Struts 2

DZone's Guide to

Modularity With Apache Struts 2

· Web Dev Zone ·
Free Resource

Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

Modularity with Apache Struts 2 doesn't just make sense if you want to create a web application with a set of web pages. But if your intent is to create a Single Page Application which interacts with Node.js through REST while having a Java EE back-end at your disposal, then my previous article was the entry point for you to know how to bootstrap angular.js, backbone.js, ember.js or your favorite client-side framework in your index page. And if all was conditionally dictated, then the JSP Expression Language would have come very handy.

index.jsp

<!doctype html>
<html lang="en" ng-app>
  <head>
    <meta charset="utf-8">
    <title>My HTML File</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
    <script src="bower_components/angular/angular.js"></script>
  </head>
  <body>

    <p>Nothing here {{'yet' + '!'}}</p>

  </body>
</html>

And for a team of several web developers in charge to create a web application made by a set of web pages, it is just good news to hear that Apache Struts 2 provides out the box a clear decomposition of a web application with the concept of packages which are a way to group actions, results, result typesinterceptors and interceptor-stacks into a logical configuration unit.

Creating a CRM Application

We will wait no longer to see that in action and to create a CRM application with three packages to manage customers, products and orders.

struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 
Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>

    <package name="customers" namespace="/customers" extends="struts-default">

    </package>

    <package name="products" namespace="/products" extends="struts-default">

    </package>

    <package name="orders" namespace="/orders" extends="struts-default">

    </package>

</struts>

And if the use of a single configuration file has broken here our intent to bring a clean separation between the developers, then we are only on the right way to modularity and to let each developer to manage its own module and by default, a module can be added to an application by placing a struts.xml and related classes into a jar on the classpath and it can extend a root package.

root

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 
Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

    <package name="root" namespace="/" extends="struts-default">

        <action name="index" class="WelcomeAction">

            <result>index.jsp</result>

        </action>

    </package>

</struts>

Module Customers

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 
Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>

    <package name="customers" namespace="/customers" extends="root">

    </package>

</struts>

But the drawbacks of this approach are that a module is not self-contained since its web pages, its css, js and images are stored out of the jar and no one can add, remove or update a module at runtime to enable a live development. Then, we must put ourselves towards the road to design a dynamic system and to eliminate the need of a full redeployment of the web application. And we can take inspiration into Node.js to create a modules directory where a self-contained module can be installed and its configuration loaded in our context at startup to get started. We can also create an integration with the jsr-223 to write our actions with Apache Groovy or any compliant scripting language plugged in.

Modules Directory

Image title

StartupListener

@WebListener
public class StartupListener implements ServletContextListener {

 @Override
 public void contextInitialized(ServletContextEvent event) {
  FilterRegistration struts2 = event.getServletContext().addFilter("struts2",
  org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.class);
  struts2.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST,
  DispatcherType.FORWARD),true, "/*");
  struts2.setInitParameter("config",getConfigurations(event.getServletContext()));
 }

 private String getConfigurations(ServletContext context) {
  String config = "struts-default.xml,struts-plugin.xml,struts.xml";
  String root = new File(context.getRealPath("/")).getAbsolutePath();
  File directory = new File(root+"/modules");
  for(File folder : directory.listFiles()) {
    if(folder.isDirectory()) {
      config += ","+new File(folder+"/struts.xml");
    }
  }
  return config;
 }
 @Override
 public void contextDestroyed(ServletContextEvent event) {

 }

}

And the next thing to do before closing this part is to at least display the index page of a module and to later add the missing pieces in order to reach a higher design.

Module Customers

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 
Struts Configuration 2.0//EN" 
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>

    <package name="customers" namespace="/customers" extends="root">

       <default-action-ref name="index"></default-action-ref>

        <action name="index">
          <result>/modules/customers/index.jsp</result>
        </action>

    </package>

</struts>

index.jsp

<!DOCTYPE html>
<html>
  <head>
    <title>Module Customers</title>
    <style>
      h1 {
          margin-top:17%;
          padding-left : 50px;
          line-height : 55px;
      }

    </style>
  </head>
  <body>
    <h1 align="center">Module Customers</h1>
  </body>
</html>

And with this configuration above, the module is accessible through the context/customers/ or context/customers/index url. But if you want it to be accessible only through the context/customers url then you must have its reference in the root package and it can be done with an action.

root

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 
Struts Configuration 2.0//EN" 
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
  <package name="root" namespace="/" extends="struts-default">

     <action name="index" class="WelcomeAction">
        <result>index.jsp</result>
     </action>

      <action name="customers">
        <result>/modules/customers/index.jsp</result>
     </action>

  </package>

</struts>

And all can be made generic with a regular expression for all of the modules if we map their urls to their directory names and to enable this feature, set the following constant in struts.xml.

<constant name="struts.patternMatcher" value="regex"/>

root

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 
Struts Configuration 2.0//EN" 
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>

  <constant name="struts.patternMatcher" value="regex" />

  <package name="root" namespace="/" extends="struts-default">

     <action name="index" class="WelcomeAction">
        <result>index.jsp</result>
     </action>

      <action name="{module}">
        <result>/modules/{1}/index.jsp</result>
     </action>

  </package>

</struts>

Module Customers (struts.xml optional)

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 
Struts Configuration 2.0//EN" 
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>

    <package name="customers" namespace="/customers" extends="root">

    </package>

</struts>



Deploying code to production can be filled with uncertainty. Reduce the risks, and deploy earlier and more often. Download this free guide to learn more. Brought to you in partnership with Rollbar.

Topics:
struts 2 ,modularity ,java ee ,node.js ,javascript ,rest

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}