Passing User Context When Invoking ADF BC SOAP Web Services

Introduction

ADF web applications often use session-scoped user context information that is also used in the ADF Business Components (ADF BC) layer. For example, the user language might be used to query language-specific data from the database. This article explains how this user context can be set up when accessing the ADF BC layer through its SOAP-based web service layer rather than through a web application.

Main Article

To access session information in the ADF BC layer, ADF developers typically use the ADFContext object. For example, when the preferred user language is stored on the HTTP Session under the key “userLanguage”, this information can be accessed in the ADF BC layer using the following statement:

String language = (String)ADFContext.getCurrent().getSessionScope().get("userLanguage");

This value can then be used in the prepareSession method of an application module method to set the user language in a database context package. This saves us the tedious work of passing in the user language as a bind variable to every database query.

When accessing an application module as a SOAP web service using the SDO service interface we need to set up the same data in the ADFContext to ensure the database queries can be executed correctly.This can be done by creating a SOAP Message Handler. A SOAP message handler provides a mechanism for intercepting the SOAP message and header in both the request and response of the Web service. To add a SOAP message handler to our ADF BC service interface, we add the HandlerChain annotation at the top of our application module SDO service class:

@Interceptors(
  { ServiceContextInterceptor.class})
@Stateless(name="oracle.ateam.hr.soapdemo.model.common.HRServiceServiceBean"
           , mappedName="HRServiceServiceBean")
@Remote(HRServiceService.class)
@PortableWebService(targetNamespace="/oracle/ateam/hr/soapdemo/model/common/"
                    , serviceName="HRServiceService",
  portName="HRServiceServiceSoapHttpPort"
, endpointInterface="oracle.ateam.hr.soapdemo.model.common.serviceinterface.HRServiceService")
@HandlerChain(file = "handlers.xml")
public class HRServiceServiceImpl extends ServiceImpl implements HRServiceService
{

The HandlerChain annotation specifies an XML configuration file that you can use to specify one or more handlers for your web services. The value of the file attribute is a URL, which may be relative or absolute. Relative URLs are relative to the location of the Java Web Service class that contains the annotation. We used a relative path, so we need to store the handlers.xml file in the same location as the Java class. Here is the content of the handlers.xml file:

<?xml version="1.0" encoding="windows-1252" ?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
  <handler-chain>
    <handler>
      <handler-name>UserContextHandler</handler-name>
      <handler-class>oracle.ateam.hr.soapdemo.model.UserContextHandler</handler-class>
    </handler>
  </handler-chain>
</handler-chains>

The UserContextHandler class needs to implement the SOAPHandler<SOAPMessageContext> interface and looks like this:

package oracle.ateam.hr.soapdemo.model;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;

import oracle.adf.share.ADFContext;

public class UserContextHandler implements SOAPHandler<SOAPMessageContext> {

    public boolean handleMessage(SOAPMessageContext mc) {
        Map headers = (Map) mc.get(mc.HTTP_REQUEST_HEADERS);
        Object lang = headers.get("Language");
        if (lang != null && lang instanceof List ) {
            String language = (String) ((List) lang).get(0);
            ADFContext.getCurrent().getSessionScope().put("userLanguage", language);
        }
        return true;
    }

    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }

    public void close(MessageContext mc) {
    }

    public boolean handleFault(SOAPMessageContext mc) {
        return true;
    }
}

You might be a bit puzzled by the cast to java.util.List, this i needed because the value of each header parameter is contained by a List object for some strange reason. As you can see,we can directly access and use the ADFContext object here which makes it all very easy.To test whether everything works, we first create a prepareSession method in the application class like this:

@Override
public void prepareSession(SessionData sessionData) {
    String language = (String) ADFContext.getCurrent().getSessionScope().get("userLanguage");
    System.err.println("USER LANGUAGE: "+language);
    // TODO add code to set language as DB context
    super.prepareSession(sessionData);        
}

Now we can use a tool like SOAP UI to call the web service:

SoapUi

Note the header parameter Language that we configured. After we invoked the web service using SOAP UI,the JDeveloper log proves that the language value is available in the prepareSession method:

jdevlog

That’s all, a simple yet powerful technique to reuse existing view objects that rely on ADFContext session information with SOAP-based web services.

You can download the sample project here.

 

 

 

 

 

Add Your Comment