Stateful Web Services mit JAX-WS

Ohne die Sinnhaftigkeit von zustandsbehafteten Web Services vorab diskutieren zu wollen sind hier 3 Möglichkeiten genannt solche mit Hilfe von JAX-WS bzw. dessen Referenzimplementierung umzusetzen.

Variante 1: Direkter Zugriff auf die HTTP-Session über den WebServiceContext

Bei einem Web Service, der auf HTTP und SOAP aufsetzt und in einem Servlet-Container läuft, war es natürlich schon immer möglich die HTPP-Session des Servlet-Containers als Zustandsspeicher zu verwenden. Zugriff auf besagte Session bekommt man über den WebServiceContext, welcher über die Annotation @Resource injected werden kann. Ein Beispiel zeigt das folgende Listing:

@WebService
public class StatefulHello1 implements StatefulHello {

  @Resource
  private WebServiceContext context;

  @WebMethod
  public int getCount() {
    MessageContext mc = context.getMessageContext();
    HttpServletRequest request = (HttpServletRequest) mc
      .get(MessageContext.SERVLET_REQUEST);
    HttpSession session = request.getSession();

    Integer count = (Integer) session.getAttribute("count");
    if (count == null) {
      count = new Integer(0);
    }
    count = new Integer(count.intValue() + 1);
    session.setAttribute("count", count);

    return count;
  }
}

Die Verwaltung der HTTP-Session auf der Serverseite übernimmt dann wie gewohnt der Servlet-Container. Dieser verwendet dafür Cookies oder URL-Rewriting. Damit dies funktioniert muss ein Client ebenfalls eine entsprechende Session-Verwaltung bereitstellen (bzw. mit Cookies umgehen können). Dies kann für den Client am RequestContext des BindingProvider über ein Flag aktiviert werden:

StatefulHello port = StatefulHelloClient1.getPort();
BindingProvider bp = (BindingProvider) port;
Map<string, Object> ctx = bp.getRequestContext();
ctx.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);

int result = cort.getCount();

Mehr zu dieser Variante findet man in einem Blogartikel von Rama Pulavarthi.

Variante 2: Per Annotation eine Instanz pro HTTP-Session

Die im ersten Beispiel gezeigte Möglichkeit lässt sich mit einer Extension der JAX-WS Referenzimplementierung (ab Version 2.1) auch etwas eleganter ausdrücken:

@HttpSessionScope
@WebService
public class StatefulHello2 implements StatefulHello {

  private int count = 0;

  @WebMethod
  public int getCount() {
    return count++;
  }
}

Die Annotation @com.sun.xml.ws.developer.servlet.HttpSessionScope sorgt dafür, dass die JAX-WS RI pro HTTP-Session eine Instanz des Web Service erzeugt und man nicht selbst über den WebServiceContext und die HttpSession den gewünschten Zustand verwalten muss. Möglich wird dies durch einen entsprechenden InstanceResolver im Hintergrund. Dabei handelt es sich um einen Erweiterungsmechanismus den die JAX-WS RI mitbringt.

Mehr dazu steht in einem Blogartikel von Kohsuke Kawaguchi. Natürlich ist für die erfolgreiche Nutzung dieser Variante ebenfalls ein Client notwendig, der Cookies verwalten kann.

Variante 3: Protokollunabhängig über WS-Adressing

Seit JAX-WS RI 2.1 EA2 unterstützt die Referenzimplementierung einen Mechanismus, der auf WS-Addressing aufsetzt und damit den Anspruch erhebt auch über Herstellergrenzen hinweg kompatibel zu sein und protokollunabhängig zu arbeiten. Dies wird ebenfalls in einem Blogartikel von Kohsuke Kawaguchi erwähnt und findet sich auch im User Guide zu JAX-WS RI wieder.

Im ersten Schritt annotiert man den Web Service wie in folgendem Beispiel und definiert eine öffentliche und statische Referenz vom Typ StatefulWebServiceManager.

@Stateful
@WebService
@Addressing
public class StatefulHello3 implements StatefulHello {

  public static StatefulWebServiceManager<statefulHello3> MANAGER;

  private int count = 0;

  @WebMethod
  public int getCount() {
    return count++;
  }
}

Anschließend bedarf es eines zweiten Web Service, der eine Methode bzw. Operation bereitstellt, die folgendermaßen implementiert ist:

@WebService
public class StatefulHello3Factory {

    @WebMethod
    public synchronized W3CEndpointReference login() {
        StatefulHello3 service = new StatefulHello3();
        return StatefulHello3.MANAGER.export(service);
    }
}

Über diesen zweiten Web Service kann dann jeweils ein “zustandsbehafteter Endpunkt” erzeugt werden. Der Client für einen solchen muss nun allerdings WS-Addressing unterstützen um die eindeutige ID, welche im Addressing-Header spezifiziert ist, korrekt zwischen den einzelnen Calls an den eigentlichen Web Service zu transportieren.

Fazit

Dass zustandsbehaftete Web Services durchaus kritisch zu sehen sind ist nicht neu. Im Allgemeinen werden Argumente wie mangelnde Skalierbarkeit oder Probleme beim Clustering angebracht. Ich möchte an dieser Stelle nur kurz 2 Quellen dazu nennen:

Im letzten Kommentar einer Forumsdiskussion gibt jemand zu bedenken, dass die 3. Variante es wohl nicht bis in die JAX-WS Spezifikation geschafft hat. Das ist insoweit korrekt, als dass es sich um eine Extension handelt, die dort nicht erwähnt wird. Sie sollte allerdings unter Glassfish/Metro funktionieren (genauso wie Variante 1 & 2).

Daneben ist ein Hinweis bei Oracle (A Note About the JAX-WS RI @Stateful Extension) interessant: Weil es sich um eine herstellerspezifische Erweiterung handelt, werde gerade die 3. Variante nicht unterstützt. Nach meiner eigenen Erfahrung scheint WebLogic in Version 10.3 aktuell nur Variante 1 zu unterstützen.

Short URL for this post: http://wp.me/p4nxik-hU
This entry was posted in Did you know?, SOA / Webservices and tagged , , . Bookmark the permalink.