About
fv_2007
Agile innovative developer with deep insight into lots of platforms, technologies and protocols. Absolute “early adopter” in Web 2.0 technologies and more. Large professional network and eagerly talking about architecture, strategy, design patterns, restful ressources, object-oriented thinking and modeling languages such as PML. Special interest in programminglanguages constructs, knowledge on languages like Smalltalk, Erlang, Java, Clojure, Scala, Ruby... read more
Follow
Feedfavicon
Comments
Language

Mocking SOAP onto Resources November 06, 2009 17:28 10 months ago

Titlen på denne post kan måske forekomme misvisende eller ligefrem tvetydig idet den blander to paradigmer, to protokoller og to praktiske modeller. Men netop denne dualisme og tværgående egenskaber kan resultere i en styrke. Abstrakt handler dette blot om at se på services som resource. Denne post har absolut intet at gøre med REST men kun om brug og kombination af enkle og gode egenskaber omkring HTTP Applikations Protokollen.

For at komme i gang vil jeg lige sige et par ord om Mocks. Mocks er objekter der indenholder en forventet reaktion. Man kunne fx mock’e information om processen af købe en kop kaffe i nogle trin uden af kende hele den samlede proces til bunds. På den måde ville kaffe tørstige sjæle kunne benytte cafe-service’en ved at bestille forskellige type af kaffe. Endda før kaffebaren er helt færdig. En Stub derimod ville i samme senarie blot returnere den samme kedelige kop kaffe hvergang uden nogen hensyntagen til modtageren.

Hvad har kaffe med SOAP at gøre? Jo, jeg har modtaget en række løstformulererede krav til en SOAP service der kan erstatte en ESB. Kravende indeholder ord som enkelhed, robusthed og fleksibel. Sjovt nok hænger de egenskaber ikke naturligt sammen med de flestes opfattelse af SOAP og WS-. Jeg må blankt indrømme at jeg normalt løber min vej når talen kommer ind på SOA, SOAP og WS- dødsstjerne teknoliger

Men på samme måde som en skatterevisor til fest er min interesse altid på ca. 50 procent når noget er dukket op på området. Min holdning er blot at jeg kan tilføre mere værdi med andre metoder.

Som udgangspunkt enig med med folkene bag http://soa-manifesto.org om at SOA som arkitektur er en god ting og SOAP som protokol er noget markværk. Derfor vil jeg bryde alle (mine) principper og forsøge at angribe med ord som “Brute Force” og WS-* Duck-Typing Når man operaer med “Brute Force!” metoden er det næsten det samme bare med mere traditionelle værktøjer.

Paul Sandoz fra SUN, som i øvrigt sidder i ekspertgrupperne JSR-154, JSR-224, JSR-311 har på en konference fortalt mig at man kan se på enhver SOAP besked som en resource. Og hvis ikke er der et design issue. Derfor vil jeg i dette forsøge at udstille et SOAP endpoint som en resource. Eller rettere, forbrugeren vil ikke opdage en forskel men blot kalde en service som sædvandeligt men for service udbyderen er der en væsenlig forskel. Klienten kalder med JSR-244 (JAX-WS) og serveren svare med JSR-311(JAX-RS).

Med denne metode kan jeg overholde WS-* Ducktyping principperne og på samme tid arbejde highlevel mod HTTP protokollen uden de afledte effekter som codeeksplosion, classeksplosion interoperability og de andre aspekter der bunder i en tæt bundet SOAP protokol. SOAP definere sin egne protokol ovenpå HTTP protokollen og den abstraktion er vigtig set ud fra det perspektiv at man kan skifte transportlag til JMS eller noget andet. Men hvis man kun bruger HTTP som transport er det naturligvis kun en abstraktion der altid vil ende med Law of leaking abstractions by Joel Spolsky

I denne context er Highlevel er lig med det Uniforme Interface fra REST der tilbyder ensartede operationer på tværs af alle komponenter. Det er en kæmpe fordel frem for SOAP protokollen hvor hver WSDL definere et specifikt service interface. Med SOAP arbejder man med et lag ovenpå http protokollen mens man med REST arbejder med protokollerne og derfor siges det at abstraktionen mellem de to fremgangsmåder er på forskellige plan.

Resoucen

@Path("/Customer")
  public class CustomerResource {

  @POST 
  @Consumes("appliction/soap-xml;charset=UTF-8")
  @Produces("appliction/soap-xml;charset=UTF-8")
  public String method(String body) {
    ...
  }
}

Denne resource klasse repræsentere en kunde. Path symbolet viser den relative URI til ressourcen. For at kunne attakere denne metode som en resource kan man fx sende et HTTP (POST) med den rette content-type (Consumes) i HTTP headeren. Metoden modtager en SOAP besked og returnere en SOAP (@Produces) besked. Hvordan beskeden teknisk behandles i metoden er ligegyldigt. For en brugern af denne SOAP service er det transparent at der benyttes ressource. Ved en fejl i metoden returenes naturligvis en “optional SOAP Fault element”.

Det er muligt at skabe en @Singelton instans ved at tilføre en annotation. Således finder der kun den samme ressource for alle consumere og det er muligt at udnytte den feature på flere måder end jeg kan komme på her. Ved Mocking er det selvvalgt en naturlig ting idet man kan ændre indhold på ressource output i køretid. Under normale forhold vil vi naturligvis stræbe efter stateless resourcer for at bibeholde egenskaber som skalerbarhed, samtidighed osv.

Indtil nu er der kun brugt annoteringer fra API’et på en POJO Java klasse men det er tid at vælge hvilken implementation der skal bruges. Jeg bruger JAX-RS API og SUN’s Jersey 1.1 reference implementation fordi den er langt den mindste jeg kender og har en opstartstid jeg kan udholde. Ikke et ond ord om Apache CXF der har suppport for JAX-RS i sin JAX-WS implementation. Registrering og brug af denne resource kan ske på flere måder, både standalone og i container. Jersey har en super cool scanner der selv register klasser efter annotationer og binder dem til det rigtige endpoint der dekoreres dels i Web.xml og dels direkte i annotationer. Det bliver ikke bedre end det. :-)

En af de stærke sider ved Jersey er deres client interface. Kan ikke lade være at vise en stumt kode der simulere et SOAP WS kald mod en ESB eller whatever du nu har. Læg især mærke til den dejligt Java Fluent Interface måde man opbygger sin request på, næsten i DSL stil. Mange forskellige teknologier har adopteret og overholder HTTP, Servlets og JAX-WS. Det betyder at man kan bruge en ressource som et endpoint for alle komponenter der taler samme sprog. De vil aldrig vide hvad der ramte dem. På samme måde kan man bruge klientdelen til at invokere fx ESP endpoints. Det er faktisk sjovt.

  Client client = Client.create();
  WebResource endpoint = client.resource(uri);

  ClientResponse response = endpoint.
      type("application/soap+xml;charset=utf-8"). 
      accept("application/soap+xml;charset=utf-8").
      post(ClientResponse.class, xml);

   int status = response.getStatus();
   String s = response.getEntity(String.class);

Jersey og hele REST tankegange er baseret tæt omkring HTTP Applikations Protokollen. Man er “ON The Web” eller en del af The Semantic Web og ens applikation kan understøtte Linkede Data Med disse tanker og metoder smelter applikationen sammen med internettet.

Den forrige resource “/resource/Customer” vil blive ramt af SOAP klienter. Men jeg kan ikke dy mig for at lade den samme resource blive brugt af simpelt AJAX (Asynchronous JavaScript and XML). Med AJAX kan man fra Javascript kommunikere med en server gennem XMLHttpRequest objektet uden at reload HTML siden. En anden ting er, at man kan kontroller stort set alle de ekstra header informationer som overføres til serveren hvilkent betyder at man kan rammer en resource fx med en bestemt content-type.

new Ajax.Request("/resource/Customer", {
  method:'GET',
  asynchronous:true,
  requestHeaders: ['Content-Type', 'text/html;charset=UTF-8'],
  onSuccess: function(transport){
    $('message').update(transport.responseText);
  },
  onFailure: function(transport){
    $('message').update(transport.responseText);
  }
});

Denne metode laver en GET på ressourcen Custormer og forventer at modtage en repræsentation af dennes response i form af HTML. Hvis man skal bruge JSON skal man kun ændre content-type til “applikation/JSON”. Hvis man i stedet vil POST’e et ændret indhold ind i ressourcen kan man bruge denne form:

new Ajax.Request("/resource/Customer", {
  method:'POST',
  asynchronous:true,
  requestHeaders: ['Content-Type', 'appliction/xml;charset=UTF-8'],
  postBody: $F('body'),
  onSuccess: function(transport){
   ...

Dette POST bindes sammen med ressourcen gennem POST med content-type ‘appliction/xml;charset=UTF-8’ og forventer at modtage en Customer i XML format som input i HTTP body. Hvis ressourcen ikke har implementeret denne indgang svares med Status Code 415 Unsupported Media Type

Fik jeg så hvad jeg kom efter? Ja, det synes jeg. Ved at mappe ressource til simulering af SOAP endpoints og Servlets side om side fik jeg en cross platform, cross service implementation og mere clean snitflade uden de logiske layer som jeg hader. Håber bare at mine kravsstiller ikke vil skifte transport metode. Anyways, det sker nok alligevel aldrig.

At efterleve DuckTyping principperne er en fornøjelse og jeg har virkelig hygget mig undervejs. Tak til Paul Sandoz for at starte den ide i mit hoved i 2007.


By Frank Vilhelmsen - 3 tags: roa soa ws* - Add comment