|<<>>|225 of 274 Show listMobile Mode

Session and Requests in HiveMind

Published by marco on

This article was originally published on the Encodo Blogs. Browse on over to see more!


If you are not already familier with HiveMind, read Setting up a Service in HiveMind for an introduction.[1]

Almost every application is going to need to have information that is session-specific. This is accomplished by adding a member to Tapestry’s application objects list and assigning it the proper scope. With a scope of “session”, HiveMind makes sure that each session in the web application has its own copy.

  <contribution configuration-id="tapestry.state.ApplicationObjects">
    <state-object name="CustomSessionService" scope="session">
      <create-instance class="tapestry.CustomSessionService"/>
    </state-object>
  </contribution>

The tag names are quite straightforward in this case, with a CustomSessionService instantiated for each session.

A Note on Auto-Wiring

There is, within HiveMind, a concept known as “auto-wiring”, which purports to automatically make the connection between services based on interfaces: if one HiveMind service has a setter accepting an interface as declared by one and only one other HiveMind service, it is automatically applied to that service. That is, assume that the following service is the only one providing the ICustomApplicationService interface to the application:

<service-point id="CustomApplicationService" interface="tapestry.ICustomApplicationService">

Any other service declared in HiveMind, whose implementation includes a public setter method taking a parameter of type ICustomApplicationService (as shown below), will have this service automatically injected into it via that method.

  public void setCustomApplicationService(ICustomApplicationService _service) {
    applicationService = _service;
  }

The exact rules for setter naming aren’t known[2]; it is recommended that you stick to using the id of the service prepended with “set”.

Though we have seen auto-wiring work and it works consistently, it’s not always immediately obvious where it won’t work; generally, we found it’s much better to just declare the connection explicitly for two reasons:

  1. It’s self-documenting; maintenance developers can look in the HiveMind file to see which objects are connected to each other instead of relying on magic to connect the objects
  2. Though it’s more work to declare a factory, it’s at least straightforward once you know how. It’s generally much faster (and far less frustrating) than trying to figure out why the auto-wiring magic didn’t work.

Integration with Services

If the session needs further configuration or connection with other services, the following, though logical (especially in light of how services are configured), does not work:

  <contribution configuration-id="tapestry.state.ApplicationObjects">
    <state-object name="CustomSessionService" scope="session">
      <construct object="service:CustomSessionService">
        <set-service property="applicationService" service-id="CustomApplicationService" />
      </construct>
    </state-object>
  </contribution>

The element <construct> is not allowed within a state-object declaration. Instead, as with services, the state-object must be created using a factory. If you’re already familiar with the way service factories are declared, declaring a state object factory and connecting it to the session object is straightforward.

  <service-point id="QMSSessionServiceFactory" interface="org.apache.tapestry.engine.state.StateObjectFactory">
    <invoke-factory>
      <construct class="tapestry.CustomSessionServiceFactory">
        <set-service property="applicationService" service-id="CustomApplicationService" />
      </construct>
    </invoke-factory>
  </service-point>

  <contribution configuration-id="tapestry.state.ApplicationObjects">
    <state-object name="CustomSessionService" scope="session">
      <invoke-factory object="service:CustomSessionServiceFactory" />
    </state-object>
  </contribution>

For completeness, we’ll include an example implementation of the state object factory to show which function needs to be overridden:

public class CustomSessionServiceFactory implements StateObjectFactory {
  private ICustomApplicationService applicationService;

  public Object createStateObject() {
    CustomSessionService result = new CustomSessionService();
    result.setApplicationService(getApplicationService());
    return result;
  }

  public ICustomApplicationService getApplicationService() {
    return applicationService;
  }

  public void setApplicationService(ICustomApplicationService _service) {
    applicationService = _service;
  }
}

In this case, it’s the createStateObject() function that must be implemented in order to create the session service. HiveMind sets the application service using setApplicationService() (there is an error if this is not defined because the HiveMind configuration for the factory references the applicationService property) and the factory simply passes the application service to the session service when it creates it. Granted, it would have been much more intuitive to simply be able to specify this all in the HiveMind configuration (as attempted above), but at least there is a workaround.

Per-Request Objects

Web applications handle browser requests, so they generally create a small environment of objects to handle each request. Each request is handled in its own thread; the application can use HiveMind to create and connect these types of objects as well. For example, if you’re working with Hibernate, each request needs its own session—it’s a bad idea to keep them open across multiple requests—so you can delegate creation of this session to HiveMind. Unfortunately, the following attempt won’t get you very far at all (though it is pretty intuitive):

  <contribution configuration-id="tapestry.state.ApplicationObjects">
    <state-object name="CustomHibernateSession" scope="request">
      <create-instance class="tapestry.CustomHibernateSession"/>
    </state-object>
  </contribution>

Instead, per-request objects are declared as full-fledged services, but use a different invokation model. The declaration looks like that for any other service, but with one small (highlighted) difference:

  <service-point id="CustomHibernateSession" interface="tapestry.ICustomHibernatSession">
    <invoke-factory model="threaded">
      <create-instance class="tapestry.CustomHibernateSession"/>
    </invoke-factory>
  </service-point>

Remember that the names for state objects and ids for services can be anything, as long as they are unique within the union of all HiveMind configurations in your application (including those from Tapestry and any contributions your application uses).

Conclusions

As we’ve seen in both this article and Setting up a Service in HiveMind, HiveMind can be incredibly useful for dynamic configurations like web applications. However, it’s difficult to build on previous knowledge when tackling a new problem—the syntax for each type of situation is slightly different. Once you know how, it’s not bad, but getting there can be quite a battle.


[1] The examples in this article assume the same module declaration as in that article, namely, using id = com.encodo.customer.project
[2] To this author anyway; I’m sure someone knows, and, with the source available, anyone with enough determination can find out.

Using Java 1.5, Tapestry 4.02, HiveMind 1.1.1