Don't Introduce Concurrency Issues

The application may service requests from many users at the same time. Even when a single user is active, the application may still receive concurrent requests for several pages that are presented to that user in the tabbed user interface. At run-time, only one instance of each renderer plug-in class is created for each domain or style. The application may use the same plug-in instance to service concurrent requests from one or more users. This places some restrictions on the implementation of a renderer plug-in class to avoid concurrency problems. The restrictions also apply to all other kinds of domain and style plug-ins, as they share the same life-cycle as renderer plug-ins.

Maintaining state information within a plug-in instance will cause concurrency problems. A developer may introduce a dependency on state information when factoring a large render method into smaller, more manageable, private methods. If, instead of passing all information between methods using method arguments, the developer passes information through fields of the plug-in class, concurrency defects will arise. Don't Introduce Concurrency Issues shows such a defect.

Figure 1. A Plug-in Class with a Concurrency Defect
          public class DefectiveEMailAddressViewRenderer
       extends AbstractViewRenderer {
           private String emailAddress; 
          public void render(
        Field field, DocumentFragment fragment,
        RendererContext context, RendererContract contract)
        throws ClientException, DataAccessException,
               PlugInException {

    emailAddress = context.getDataAccessor()
       .get(field.getBinding().getSourcePath());

    Document doc = fragment.getOwnerDocument();

    Element span = doc.createElement("span");
    span.setAttribute("class", "email-container");
    span.appendChild(createAnchor(doc));
    fragment.appendChild(span);
  }

  private Element createAnchor(Document doc) {
    Element anchor = doc.createElement("a");
    anchor.setAttribute("href", "mailto:" + emailAddress);

    Element img = doc.createElement("img");
    img.setAttribute("src", "../Images/email_icon.png");
    anchor.appendChild(img);

    anchor.appendChild(doc.createTextNode(emailAddress));
    return anchor;
  }
}

The DefectiveEMailAddressViewRenderer class is similar to the EMailAddressViewRenderer class developed in An E-Mail Address Widget. The defective class has a createAnchor method to organize the code for improved readability. However, rather than pass the e-mail address value as a method argument, the e-mail address is defined as a field of the class that is set by the render method and read by the createAnchor method. At run-time, there may be concurrent requests for pages containing e-mail addresses, so the render method of a single instance of the renderer plug-in for e-mail addresses may be invoked from more than one thread. This can lead to a defect where the shared field value becomes corrupt.

For example, thread T1 services a request from user U1 and thread T2 services a request from user U2. T1 calls the render methodon the same plug-in instancejust before T2 does. T1 sets the emailAddress field value to e-mail address E1and then T2 immediately sets the field to E2. Now, when T1 invokes createAnchor, e-mail address E2 will be rendered and shown to user U1. This may not be a serious problem for e-mail addresses, but the same defect could lead to unwanted leaking of more sensitive information. In the case of edit-renderer plug-in initializing form field values when modifying entities, the problem could also result in incorrect values being written to the database.

It is also important to note that concurrency problems do not necessarily arise because there are two or more users active; they arise because there are two or more requests active. With the tabbed user interface, it is very likely that a single user can trigger concurrent requests for pages. Do not dismiss potential concurrency problems on the mistaken assumption that data that is local to a user, such as data stored in Java EE session attributes, is immune from such problems.

The remedy for this problems is simple: do not use fields of a class to pass information between methods; use the methods' arguments instead. Don't Introduce Concurrency Issues shows the alternative implementation that has no concurrency defect because the e-mail address value is passed as an argument to the createAnchor method.

Figure 2. A Plug-in Class without a Concurrency Defect
public class DefectiveEMailAddressViewRenderer
       extends AbstractViewRenderer {

  public void render(
        Field field, DocumentFragment fragment,
        RendererContext context, RendererContract contract)
        throws ClientException, DataAccessException,
               PlugInException {

    String emailAddress = context.getDataAccessor()
       .get(field.getBinding().getSourcePath());

    Document doc = fragment.getOwnerDocument();

    Element span = doc.createElement("span");
    span.setAttribute("class", "email-container");
    span.appendChild(createAnchor(doc, emailAddress));
    fragment.appendChild(span);
  }

  private Element createAnchor(
        Document doc, String emailAddress) {
    Element anchor = doc.createElement("a");
    anchor.setAttribute("href", "mailto:" + emailAddress);

    Element img = doc.createElement("img");
    img.setAttribute("src", "../Images/email_icon.png");
    anchor.appendChild(img);

    anchor.appendChild(doc.createTextNode(emailAddress));
    return anchor;
  }
}

In general, avoid fields of a plug-in class unless they are constants declared static and final. Carefully consider the potential for concurrency defects before considering the introduction of any non-constant fields and must never introduce fields simply to shorten the argument lists of private methods.

The fields of a plug-in class are the most obvious place to store state information during rendering. However, a developer might store state information in other places, such as in attributes of the Java EE session or application, in ad hoc data caches and in helper classes. In introducing any such state storage, consider concurrency issues with the same care given to fields of a plug-in class.