Follow Us on Twitter

Webservices met JAX-WS en ADF Business Components

Juni 2014- In moderne architecturen spelen webservices een belangrijke rol. Dit geldt zeker bij SOA/BPM projecten, waar webservices worden gebruikt om de gewenste processen te implementeren. In dergelijke projecten is directe toegang tot databases bijvoorbeeld vaak niet gewenst. Dergelijke functionaliteit moet dan worden gebouwd met (herbruikbare) webservices.

Er zijn veel oplossingen op internet te vinden over het bouwen van webservices met Java. De combinatie van JAX-WS en Hibernate lijkt momenteel de de facto standaard. Deze combinatie vraagt nogal wat programmeerwerk. In projecten waar met het Java Application Development Framework (ADF) van Oracle wordt gewerkt, wordt daarom vaak gekozen om ADF Business Components (ADF-BC) in te zetten voor het genereren van webservices. Bij ADF-BC is het niet vanzelfsprekend om de webservices “contract first” – dus op basis van een bestaand WSDL-contract - te ontwikkelen en dat heeft in veel SOA-projecten wel de voorkeur.

Met dit Whitebook laten we zien dat ADF-BC in combinatie met JAX-WS een goed alternatief is voor het bouwen van webservices. Deze combinatie heeft veel voordelen van ADF en maakt “contract first” ontwikkelen mogelijk.

Het Whitebook begint met een introductie over de twee gebruikte technieken. Vervolgens gaan we in op de vraag waarom de combinatie JAX-WS en ADF Business Components een goed alternatief is. Daarna wordt uitgelegd hoe je snel en eenvoudig een webservice kan ontwikkelen op basis van je eigen WSDL door deze twee technieken te combineren.

ADF en ADF Business Components

ADF is een Java EE Framework van Oracle. Het is onderdeel van het Oracle Fusion Middleware platform, waar ook bijvoorbeeld Oracle Service Bus en Oracle SOA Suite onder vallen. ADF wordt ingezet om Enterprise Applicaties mee te ontwikkelen. Het is gebaseerd op de welbekende “Model View Controller” architectuur (MVC). Binnen ADF is ADF-BC verantwoordelijk voor de Model en Controller laag. Voor de View laag heeft ADF een implementatie van Java Server Faces met een eigen set aan componenten.

ADF-BC bestaat uit drie onderdelen: entiteit, view object en een applicatie module. In de Model laag bestaat de entiteit en het view object. De entiteit is een representatie van de tabellen en velden van de database, hiermee kunnen records in de database worden aangemaakt, aangepast en verwijderd. Het view object wordt gebruikt voor het definieren van queries op de database of via de entiteit. De applicatie module is onderdeel van de Controller laag om de view objecten te presenteren naar de View laag.

Op het Oracle blog staat een mooi artikel van Arda Eralp met meer details over de verschillende onderdelen van ADF-BC (https://blogs.oracle.com/ArdaEralp/entry/adf_business_components).

JAX-WS

JAX-WS is een API dat de relatie verzorgt tussen de operatie op de webservices en de Java methode, dit is een veel gebruikte API om webservices mee te ontwikkelen. JAX-WS gebruikt JAXB, een referentie implementatie Framework, om XML met POJO’s te refereren en de uiteindelijke webservice. Met behulp van de JAX-WS API en een WSDL kunnen de JAXB POJO’s gegenereerd worden. Op deze manier kan de ontwikkelaar eerst het contract, de WSDL, definieren en afstemmen om vervolgens de implementatie te ontwikkelen. Deze manier van ontwikkelen wordt “Contract First” genoemd. Met IDE's als JDeveloper, Eclipse, IntelliJ en Netbeans kunnen met behulp van een wizard en de WSDL de JAXB POJO’s eenvoudig gegenereerd worden. Margaret Rouse heeft een goed artikel geschreven over wat JAX-WS is. (http://searchsoa.techtarget.com/definition/JAX-WS)

Contract First

Het gebruik van Contract First wordt het meest gebruikt binnen organisaties. Eerst bepaal je samen met de afnemer het contract en op basis daarvan ga je verder met de implementatie. De tegenhanger van Contract First is Contract Last, dit houdt in dat eerst de implementatie gebouwd wordt en daarop wordt het contract gegenereerd. Het nadeel van Contract Last is dat het vanuit de implementatie wordt gegenereerd met als gevolg dat wanneer een methode in de implementatie verandert het contract mee verandert. De naamgeving van de Code is ook bepalend voor de naamgeving van het contract, hierdoor wordt het lastig om aan bedrijfsstandaarden te voldoen. Het voordeel is dat het Contract automatisch genereerd wordt. Met Contract First heb je het voordeel dat je contract niet verandert tenzij je dit zelf bewust aanpast. Het nadeel is dat het gehele contract eerst beschreven moet zijn voordat de implementatie geschreven kan worden. Op de Spring site is een beschrijving met voorbeelden over het verschil Contract First en Contract Last (http://docs.spring.io/spring-ws/site/reference/html/why-contract-first.html).

Waarom de combinatie ADF-BC en JAX-WS?

De vraag die de meeste ontwikkelaars aan ons gesteld hebben is: ”Waarom de combinatie ADF-BC en JAX-WS?” “Het is toch eenvoudiger om de webservice te ontsluiten via de Applicatie Module, of om gebruik te maken van Hibernate?“. De genoemde oplossingen zijn beide een goed alternatief voor de oplossing ADF-BC en JAX-WS.

In de volgende paragrafen worden in het kort de voor- en nadelen van het ontwikkelen van een Web Service, gebruikmakend van de Applicatie Module, Hibernate en de combinatie van Applicatie Module met JAX-WS nader uitgelegd.

Applicatie Module

De applicatie module (AM) is onderdeel van ADF-BC. De AM is de controller van de datalaag, via de AM wordt de data naar buiten gepresenteerd. Met JDeveloper kan met een vinkje via het AM configuratiescherm een webservice gegenereerd worden. De webservice bevat een verzameling van operaties gebaseerd op de methodes die als public gedefinieerd zijn binnen de AM implementatie en de view objecten (VO) die in de AM configuratie geconfigureerd zijn. Het genereren van webservices scheelt veel tijd ten opzichte van het handmatig programmeren. Het nadeel is het ontbreken van controle over het contract dat wordt gegenereerd. Hierdoor is het bijvoorbeeld lastig om bepaalde naamgevingsconventies te hanteren en eventueel specifieke SOAP-headers of foutafhandeling te implementeren.

Hibernate

Het voordeel van JAX-WS met Hibernate is dat veel ontwikkelaars dit al een keer hebben uitgevoerd. Hibernate is een JPA implementatie die Object Relational mapping mogelijk maakt en conform JPA specificaties een API aanbiedt die database onafhankelijk CRUD operaties ondersteunt. Er is veel informatie te vinden op internet om deze combinatie toe te passen. Het nadeel van deze combinatie is dat er veel geprogrammeerd moet worden ten opzichte van ADF-BC. Hibernate is enkel een Object/Relatie mapping laag, terwijl ADF-BC een compleet Framework is waar validaties, business logica en UI eigenschappen eenvoudig te configureren zijn. Zoals eerder genoemd in “Wat is JAX-WS?” kan er een webservice gegenereerd worden. Daarna moet er binnen de operatie de query via Hibernate worden uitgevoerd, hierbij moet rekening worden gehouden met het gedrag van de database. De uitkomst van de query moet verwerkt worden met de objecten van het response bericht.

JAX-WS en ADF-BC

De redenen dat er voor de combinatie JAX-WS en ADF-BC is gekozen, zijn:

  • snel kunnen ontwikkelen met behulp van wizards. Je kunt een database ontsluiten zonder code te hoeven schrijven.
  • gebruikmaken van de voordelen van ADF en toch “Contract First” kunnen ontwikkelen.
  • eenvoudig aansluiten op de Data Sources die binnen Weblogic geconfigureerd en getuned worden.

Het nadeel van de combinatie JAX-WS en ADF-BC is dat er geen standaard oplossing is om deze twee technieken met elkaar te laten communiceren. Om dit voor elkaar te krijgen moet de Applicatie Module programmatisch benaderd worden. Hier is weinig documentatie over geschreven. In de volgende paragrafen wordt een voorbeeld uitgewerkt waarin de koppeling tussen JAX-WS en ADF-BC op een efficiente manier wordt gerealiseerd.

JAX-WS ADF-BC Applicatie

De applicatie wordt ontwikkeld met JDeveloper en bestaat uit twee projecten, een Model- en een webservice project. In het Model project wordt gebruik gemaakt van ADF-BC en in het webservice project wordt gebruikgemaakt van JAX-WS/JAXB.

Het Model Project

Het Model project heeft als doel het ontsluiten van een datasource door middel van entiteiten met daarop VO. De interface naar buiten wordt geregeld door de AM. Binnen AM worden specifieke methodes en VO geselecteerd die via deze module aangeroepen mogen worden. In dit Whitebook worden alleen de VO en de AM besproken en wordt uitgelegd waar je op moet letten wanneer je programmatisch gebruikmaakt van AM.

View Objecten

De kracht van ADF-BC is de snelheid waarmee resultaat wordt geleverd bij het uitvoeren van queries op de Data Source. Dit doen ze door middel van het tijdelijk opslaan van eerder uitgevoerde queries (cachen). Voor webservices kan dit in bepaalde situaties handig zijn, bijvoorbeeld operaties die een lijst met vaste waardes terug geven. Andere operaties willen dit weer niet, deze operaties bevatten verschillende parameters, die gebruikt worden bij het uitvoeren van de query. Bij VO wordt er veel gebruikgemaakt van “View Criteria”, die gevoed worden met parameters om de standaard select query aan te vullen met extra where of order by acties. Wanneer gebruik wordt gemaakt van deze “View Criteria” moet de cache ervan eerst worden geleegd alvorens de query opnieuw uit te voeren. Het cache mechanisme van ADF-BC heeft namelijk het probleem dat ook de waarde van de query wordt vastgehouden en geeft daarmee soms het resultaat van eerder uitgevoerde query.

Applicatie Module

De AM is het centrale punt van ADF-BC. Hierin wordt gedefinieerd welke queries of methodes ontsloten worden. De AM kan helemaal geconfigureerd en getuned worden om optimaal te werken. Omdat webservices zich anders gedragen dan webapplicaties is het van belang om een aantal configuratiewijzigingen door te voeren op de AM. In het bestand “bc4j.xcfg” zijn de volgende instellingen aangepast ten opzichte van de standaardinstellingen:

AppModuleConfig    
RELEASE_MODE Stateless Binnen een webservice call heb je geen sessie.
jbo.txn.disconnect_level 1 Direct afsluiten.
java.naming.factory.initial oracle.jbo.common.
JboInitialContextFactory
 
AM-Pooling    
jbo.ampool.maxinactiveage 300000 Sluit zich af na 5 minuten van een idle sessie.
jbo.ampool.initpoolsize 10 Om direct te beschikken over een AM instantie.
jbo.ampool.maxpoolsize 360 Maximaal aantal AM instanties dat er in een pool aanwezig mogen zijn.
jbo.recyclethreshold 0 Meteen opruimen van een vrij gegeven instantie.
jbo.ampool.maxavailablesize 50 Maximaal 50 tegelijk beschikbaar.
jbo.ampool.timetolive 15000 Na 15 seconden wordt de AM opgeschoond.
jbo.doconnectionpooling True Het werken met een pool van AM.
jbo.ampool.monitorsleepinterval 120000 Om de 2 minuten wordt gekeken of er nog AM opgeruimd moeten worden.

Om gebruik te maken van de AM binnen het webservice project is er een aparte klasse gemaakt die de AM aan kan maken en weer kan opruimen als een operatie klaar is met het uitvoeren van zijn activiteit.

Hieronder volgt de uitwerking van de klasse:

package nl.whitehorses.adf.ws.adfbc.handler;

import nl.whitehorses.adf.ws.adfbc.model.HRModuleImpl;

import oracle.adf.share.logging.ADFLogger;

import oracle.jbo.ApplicationModule;
import oracle.jbo.JboException;
import oracle.jbo.client.Configuration;

public class ModuleHandlerService
{
  private static final ADFLogger LOG = ADFLogger.createADFLogger(ModuleHandlerService.class);

  public static HRModuleExtendedImpl createRootApplicationModule()
    throws Exception
  {
    ApplicationModule am = null;
    HRModuleExtendedImpl hrModuleExtendedImpl = new HRModuleExtendedImpl();

    String amDef = "nl.whitehorses.adf.ws.adfbc.model.HRModule";
    String config = "HRModuleLocal";

    try
    {
      am = Configuration.createRootApplicationModule(amDef, config);
      if (am instanceof HRModuleImpl)
      {
        hrModuleExtendedImpl.setHRModule((HRModuleImpl) am);
        return hrModuleExtendedImpl;
      }
      else
      {
        throw new Exception("Cannot create root applicationModule for HRModuleImpl", null);
      }
    }
    catch (Exception e)
    {
      throw e;
    }
  }


  public static void releaseRootApplicationModule(HRModuleExtendedImpl hrModule, boolean suppres)
  {
    //Geef de Applicatiemodule weer terug aan de pool
    try
    {
      if (hrModule != null)
      {
        if (hrModule.getApplicationModuleHandle() == null)
        {
          if (hrModule.getHRModule() instanceof HRModuleImpl)
          {
            Configuration.releaseRootApplicationModule((HRModuleImpl) hrModule.getHRModule(), true);
          }
        }
        else
        {
          Configuration.releaseRootApplicationModuleHandle(hrModule.getApplicationModuleHandle(), true);
        }
      }
    }
    catch (JboException e)
    {
      if (suppres)
      {
        LOG.severe(e.getDetailMessage());
      }
      else
      {
        LOG.severe(e.getDetailMessage());
        throw e;
      }
    }
  }
}

Binnen deze klasse zijn twee variabelen van belang, amDef en config.

amDef is een verwijzing naar de klasse van de AM.

config is een verwijzing van de te gebruiken configuratie van de AM wat beschreven staat in het configuratiebestand bc4j.xfg. Op basis van deze waardes kan er een instantie van de AM worden aangemaakt.

De ModuleHandlerService maakt daarnaast nog gebruik van een andere klasse genaamd HRModuleExtendedImpl. Hierin wordt de instantie van de AM opgeslagen tijdens het gebruik. Binnen de HRModuleExtendedImpl wordt er gebruikgemaakt van de interface van de HRModule, dit maakt het mogelijk om een andere implementatie van de AM te gebruiken voor testdoeleinden om bijvoorbeeld zonder een database de business logica te testen. Meer details hierover zullen terug komen in een van de blogs over dit onderwerp.

Het webservice project

Het webservice project bevat alleen de sources die te maken hebben met het aanbieden van de webservice interface. Het project bevat in grote lijnen twee onderdelen:

Het genereren van de JAX-WS/JAXB op basis van de WSDL.

De implementatie van de operaties.

JAX-WS/JAXB

De JAX-WS/JAXB configuratie/code wordt gegenereerd met de JDeveloper wizard “Java Web Service from WSDL...”

In de stap Specify Default Mapping Options vink je aan dat niet de standaard package wordt gebruikt maar een eigen package naam. Zorg ervoor dat de gegenereerde sources in een apart package komen, los van de specifieke sources. Dit om bij veranderingen in de WSDL de package eenvoudig te kunnen verwijderen om daarna de sources opnieuw te kunnen genereren. Dit maakt het onderhoud van de service eenvoudiger.

Java webservice

Operaties

Bij het genereren van de JAX-WS/JAXB vanuit de WSDL wordt er ook een implementatie klasse gemaakt voor de operaties. Bij het opnieuw genereren van de JAX-WS gebeurt het vaak dat de eerder gegenereerde klasse wordt gewijzigd en daardoor de service niet meer gebouwd kan worden. De oplossing hiervan is het verwijderen van de oude gegenereerde sources en daarna opnieuw generen. Om te zorgen dat de gegenereerde sources gescheiden blijven van de specifieke sources is het verstandig om per operatie een aparte klasse te maken waarin de logica van de operatie wordt geimplementeerd. Zorg dat deze sources in een package worden bewaard dat hoger is dan de root package van de gegenereerde sources, bijvoorbeeld “nl.whitehorses.employee.operatie”.

Om gebruik te kunnen maken van de AM uit het Model project moet er een relatie tussen de twee projecten gemaakt zijn binnen JDeveloper. Dit kan door in de projecteigenschappen de relatie te leggen tussen de twee projecten. Vervolgens kan er via de ModuleHandlerService een AM worden aangemaakt. In het volgende voorbeeld wordt een klasse van een operatie getoond waarin een AM wordt aangemaakt en aan het einde weer wordt vrijgegeven:

package nl.whitehorses.employee.operation;

import nl.whitehorses.adf.ws.adfbc.handler.HRModuleExtendedImpl;
import nl.whitehorses.adf.ws.adfbc.handler.ModuleHandlerService;

import nl.whitehorses.employee.converter.EmployeeConverter;
import nl.whitehorses.employee.service.FaultMessage;
import nl.whitehorses.employee.service.types.CreateEmployeeRequestMessageType;
import nl.whitehorses.employee.service.types.CreateEmployeeResponseMessageType;

import nl.whitehorses.employee.service.types.FaultMessageType;
import nl.whitehorses.object.types.Employee;

import oracle.adf.share.logging.ADFLogger;

/**
 * Operatie klasse voor het aanmaken van een Employee record in de database
 */
public class CreateEmployee
{

  private static final ADFLogger LOG = ADFLogger.createADFLogger(CreateEmployee.class);

  public CreateEmployee()
  {
    super();
  }

  /**
   * Operatie methode voor het uitvoeren van de business logica om een Employee record te
   * creëren op basis van de gegeven gegevens.
   */
  public CreateEmployeeResponseMessageType createEmployee(CreateEmployeeRequestMessageType input)
    throws FaultMessage
  {

    HRModuleExtendedImpl hrModuleExtendedImpl = null; //(1)
    CreateEmployeeResponseMessageType responseMessage = new CreateEmployeeResponseMessageType();

    try
    {
      hrModuleExtendedImpl = ModuleHandlerService.createRootApplicationModule(); //(2)
      Employee employee = EmployeeConverter.convertEmployeeTypeToEmployee(input.getEmployee());
      String employeeId = hrModuleExtendedImpl.getHRModule().createEmployee(employee);
      StringBuilder result = new StringBuilder();
      result.append("Employee with Id:").append(employeeId).append(" is created.");
      responseMessage.setResult(result.toString());
    }
    catch (Exception e)
    {
      LOG.severe(e.getMessage());
      FaultMessageType faultMessage = new FaultMessageType();
      faultMessage.setCode("EMP-0001");
      faultMessage.setMessage(e.getMessage());
      throw new FaultMessage(null, faultMessage);
    }
    finally
    {
      ModuleHandlerService.releaseRootApplicationModule(hrModuleExtendedImpl, false);
      //(3)
    }
    return responseMessage;
  }
}

In bovenstaand voorbeeld wordt eerst een HrModuleExtendedImpl - object aangemaakt en gevuld met null (1). Binnen de try wordt er een AM gecreeerd (2) en in de finally wordt deze vrijgegeven (3).

De reden om dit met try, catch en finally af te handelen is om ervoor te zorgen dat het AM object altijd wordt vrijgegeven. Wanneer het AM object niet wordt vrijgegeven, zal deze ook niet worden opgeruimd, met als gevolg dat er database connecties worden vastgehouden en het systeem traag wordt.

Conclusie

Het gebruik van JAX-WS met ADF Business Components voor het ontwikkelen van een webservice heeft het grootste voordeel wanneer er binnen de organisatie gebruik wordt gemaakt van het ADF Framework. Met ADF Business Components kan er snel een brug tussen de database en de applicatie ontwikkeld worden. Met JAX-WS heb je controle over hoe het contract eruit komt te zien, je bepaalt zelf de naamgevingsstandaarden, soap-header en de soap-fault. Samen heeft het de kracht dat er snel en volgens eigen specificaties een webservice ontwikkeld kan worden. Het brengt twee geavanceerde en krachtige technieken samen die ervoor zorgen dat je binnen een halve dag een volledig geoptimaliseerde webservice ontwikkeld hebt.

Waardering:
 

Reacties

Nieuwe reactie inzenden

De inhoud van dit veld is privé en zal niet openbaar worden gemaakt.

Meer informatie over formaatmogelijkheden

CAPTCHA
Deze vraag is om te testen of u een persoon bent en om spam te voorkomen
Image CAPTCHA
Enter the characters shown in the image.