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

Performance Is a Matter of Perspective!

DZone's Guide to

Performance Is a Matter of Perspective!

This quick tutorial shows a method for improving speed and performance, a continuation of "Using Your Favorite Dynamic Language With Apache Struts 2."

· Performance Zone ·
Free Resource

Maintain Application Performance with real-time monitoring and instrumentation for any application. Learn More!

My previous article, "Using Your Favorite Dynamic Language With Apache Struts 2" written in the continuity, is quite the demonstration that what we see is heavily dependent upon our perspective. Often, with a change, all can become quite different from what we first saw. At first glance, the skeleton of the solution, which is to create a custom ActionFactory, seemed the materialization of heaven on earth for those in search of more speed in development. 

<struts>

   <bean type="com.opensymphony.xwork2.factory.ActionFactory" class="MyActionFactory"/>

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

     <action name="login" method="login"/>

   </package>

</struts>
public class MyActionFactory extends DefaultActionFactory {
    @Override
    public Object buildAction(String url, String namespace, ActionConfig config,Map<String, Object> extraContext) throws Exception {
      Object object = buildAction(url);
      return object!=null ? object : super.buildAction(url, namespace, config, extraContext);
    }
    private Object buildAction(url) {
       String path = ServletActionContext.getServletContext().getRealPath("/");
       if(url.equals('login')) {
         File script = new File(path + "/scripts/myscript.groovy");
         if(script.exists()) {
          String name = script.getName();
          String extension = name.substring(name.indexOf(".") + 1);
          ScriptEngine engine = new ScriptEngineManager().getEngineByExtension(extension);
          return engine.eval(new FileReader(script));
         }
       }
       return null;
    }
}
class MyAction extends ActionSupport {

  def login() {

  }

}

new MyAction()

But since we are being told that evaluating a script to create an object whenever an URL is accessed can have a performance penalty, it might be interesting to ask, why we are doing it for each request and moreover now that we are in production? What are the options left for a gain of performance if there is no turning back?

Session Scope

For each user, the action associated with the requested URL will be created once and stored in the session scope for a later retrieval. It is worth noting that such is not the behavior of the DefaultActionFactory class, but to instantiate a Java class per request is fast and cheap when there is no heavy computation at construction. There is no real concern other than the performance of the Garbage Collector.

public class MyActionFactory extends DefaultActionFactory {
    @Override
    public Object buildAction(String url, String namespace, ActionConfig config,Map<String, Object> extraContext) throws Exception {
      Object object = buildAction(url);
      return object!=null ? object : super.buildAction(url, namespace, config, extraContext);
    }
    private Object buildAction(url) {
       String path = ServletActionContext.getServletContext().getRealPath("/");
       if(url.equals('login')) {
         File script = new File(path + "/scripts/myscript.groovy");
         if(script.exists()) {
          String name = script.getName();
          String extension = name.substring(name.indexOf(".") + 1);
          ScriptEngine engine = new ScriptEngineManager().getEngineByExtension(extension);
          HttpSession session = ServletActionContext.getRequest().getSession();
          Object object = session.getAttribute(url);
          if(object==null) {
            object = engine.eval(new FileReader(script));
            session.setAttribute(url,object);
          }
          return object;
         }
       }
       return null;
    }
}

If we get back to the session, it is a waste of time to implement the SessionAware interface in order to access the user's HTTP session attributes since we have already its reference in our actions :

class MyAction extends ActionSupport {

  def logout() {
    session.invalidate()
  }

}

new MyAction()

and is it wise to store a shopping cart in the session when the HTML Local Storage is a far better place?

Application Scope

The action associated with the requested URL will be created once and stored in the application scope for a later retrieval for all of the users, but this can't be done if an ActionSupport is still stateful and written like this :

class User {
  def email
  def password
}

class MyAction extends ActionSupport {

  def user = new User()

  def login() {

  }

}

new MyAction()

Instead of being stateless and written like that, if we call the groovy.json.JsonSlurper class to the rescue to go a la Node.js, to eliminate the need of a DTO, and by the way, I can't wait for the delivery of the W3C HTML JSON form submission specification which is still in a draft status, for an ease and a simplification since we still have to manage the serialization of the form by ourselves and in different ways.

const express = require("express");
const bodyparser = require("body-parser");
const app = express();
app.use(bodyparser.json());
app.post('/login', function(request, response) {
  const user = request.body;
  console.log(user.email);
  console.log(user.password);
  response.send({url:'dashboard'});
});
import static groovy.json.JsonOutput.toJson as json
import groovy.json.JsonSlurper

class MyAction extends ActionSupport {

  def login() {
     def user = new JsonSlurper().parse(request.inputStream)
     println user.email
     println user.password
     response.writer.write(json([url: 'dashboard']))
  }

}

new MyAction()
const form = $("form");
const user = {};
user.email = form.find("input[name=email]").val();
user.password = form.find("input[name=password]").val();
$.ajax({
  type: "POST",
  url: form.attr("action"),
  data: JSON.stringify(user),
  contentType : "application/json",
  success : function(response){
    location.href = response.url;
  },
  dataType : "json"
});
<form action="login">
  <input name="email" type="email"/>
  <input name="password" type="password"/>
</form>
public class MyActionFactory extends DefaultActionFactory {
    @Override
    public Object buildAction(String url, String namespace, ActionConfig config,Map<String, Object> extraContext) throws Exception {
      Object object = buildAction(url);
      return object!=null ? object : super.buildAction(url, namespace, config, extraContext);
    }
    private synchronized Object buildAction(url) {
       String path = ServletActionContext.getServletContext().getRealPath("/");
       if(url.equals('login')) {
         File script = new File(path + "/scripts/myscript.groovy");
         if(script.exists()) {
          String name = script.getName();
          String extension = name.substring(name.indexOf(".") + 1);
          ScriptEngine engine = new ScriptEngineManager().getEngineByExtension(extension);
          Map application = (Map) ActionContext.getContext().get("application");
          Object object = application.get(url);
          if(object==null) {
            object = engine.eval(new FileReader(script));
            application.put(url,object);
          }
          return object;
         }
       }
       return null;
    }
}

To finish, simplicity is the ultimate sophistication, and one single object on the server-side to handle one billion users is the true manifestation of scalability, and like for vision, Performance is a matter of perspective. Then, adding a new field for a CRUD on NoSQL must mean to do it once and for all in the UI?

Collect, analyze, and visualize performance data from mobile to mainframe with AutoPilot APM. Learn More!

Topics:
apache struts 2 ,groovy ,json ,javascript ,performance

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}