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

Deploy code to production now. Release to users when ready. Learn how to separate code deployment from user-facing feature releases with LaunchDarkly.

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>



Deploy code to production now. Release to users when ready. Learn how to separate code deployment from user-facing feature releases with LaunchDarkly.

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 }}