Category Archives: SOAP

Hosting more than one service using CXF and Jetty

This is a logical “next post” to Hosting a CXF SOAP webservice in an existing Jetty instance.

Let’s look at what’s need to host more than one service using CXF from a running instance of Jetty.

Changes to our SOAP Service

We’re going to do this by hosting our original HelloWorld service (from the previous post) with a single line change and alongside it we’ll create a REST service.

So in the AppConfig from the previous post, rename getService to getSoapServer (or whatever you like) and the code should look like this

@Bean @DependsOn( "cxf" )
public Server getSoapServer() {
   JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
   factory.setServiceBean(new HelloWorldImpl());
   factory.setAddress("/soap");
   return factory.create();
}

Apart from the method name change, we’ve added a setAddress call with a relative path. If you recall from the previous post our Jetty server will run on port 9000, i.e. http://localhost:9000 but now our SOAP service will be on http://localhost:9000/soap/HelloWorld?wsdl.

Adding a REST Service

Update the pom.xml with the following, in the properties we’ll add

<json.version>1.1.0-M1</json.version>
<glassfish.version>1.0.4</glassfish.version>

and these dependencies

<dependency>
   <groupId>javax.json</groupId>
   <artifactId>javax.json-api</artifactId>
   <version>${json.version}</version>
</dependency>

<dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-frontend-jaxrs</artifactId>
   <version>${cxf.version}</version>
</dependency>

<dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-rs-extension-providers</artifactId>
   <version>${cxf.version}</version>
</dependency>

<dependency>
   <groupId>org.glassfish</groupId>
   <artifactId>javax.json</artifactId>
   <version>${glassfish.version}</version>
</dependency>

Next we’ll create a simple REST service (I’ll actually duplicate the one from here).

So create a new Java class, mine’s named PeopleService and here’s the code

package com.putridparrot.core;

import javax.json.JsonArray;
import javax.json.Json;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/people")
public class PeopleService {
    @Produces({"application/json"})
    @GET
    public JsonArray getPeople() {
        return Json.createArrayBuilder()
                .add(Json.createObjectBuilder()
                        .add("firstName", "Brian")
                        .add("lastName", "Kernighan "))
                .add(Json.createObjectBuilder()
                        .add("firstName", "Dennis")
                        .add("lastName", "Ritchie "))
                .add(Json.createObjectBuilder()
                        .add("firstName", "Bjarne")
                        .add("lastName", "Stroustrup"))
                .build();
    }
}

within AppConfig, place the following

@Bean @DependsOn( "cxf" )
public Server getRestServer() {
   JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
   factory.setServiceBean(new PeopleService());
   factory.setProvider(new JsrJsonpProvider());
   factory.setAddress("/rest");
   return factory.create();
}

if you run the App.main, you should be able to navigate to http://localhost:9000/soap/HelloWorld?wsdl to get the SOAP service’s wsdl and http://localhost:9000/rest/people to get the results of the PeopleService call.

Just to complete the picture – the PeopleService is given a path to the REST calls. We’re simply returning JSON and so we annotate with the @Produces line. As we’re only support a GET call to this service we annotate @GET. Within the getPeople method we build up a JSON document and that’s all there is to it.

Writing the Hello World CXF webservice

Before I get into this post, I’m well aware that SOAP hasn’t been the cool kid on the block in terms of web service messaging for a while, but I’m looking at updating an old application’s webservice from a framework which is no longer supported on an older version of Java to a supported framework. So we need to keep everything working using SOAP.

Following these steps, we can get a service up and running in next to no time (ofcourse it took me far longer than “no time” to find out how to do this :)).

  • Create yourself a folder for your project, mine’s cxftest
  • Within your project’s folder create a Maven pom.xml to grab all our required dependencies we’ll require – here’s the pom
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.putridparrot.core</groupId>
      <artifactId>cxftest</artifactId>
      <packaging>jar</packaging>
      <version>1.0-SNAPSHOT</version>
      <name>cxftest</name>
      <url>http://maven.apache.org</url>
    
      <properties>
        <cxf.version>2.2.3</cxf.version>
      </properties>
    
      <dependencies>
    
        <dependency>
          <groupId>org.apache.cxf</groupId>
          <artifactId>cxf-rt-frontend-jaxws</artifactId>
          <version>${cxf.version}</version>
        </dependency>
    
        <dependency>
          <groupId>org.apache.cxf</groupId>
          <artifactId>cxf-rt-transports-http</artifactId>
          <version>${cxf.version}</version>
        </dependency>
    
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    </project>
    
  • We’re going to do everything from scratch here, but continue with the layout that Maven might generate for us, so off of your project’s folder create src/main/java/putridparrot/core
  • Now add three files, App.java, HelloWorld.java and HelloWorldImpl.java
  • Paste the following code into HelloWorld.java
    package com.putridparrot.core;
    
    import javax.jws.WebParam;
    import javax.jws.WebService;
    
    @WebService
    public interface HelloWorld {
        String reply(@WebParam(name="text") String text);
    }
    
  • Now let’s paste the following into the HelloWorldImpl
    package com.putridparrot.core;
    
    import javax.jws.WebParam;
    import javax.jws.WebService;
    
    @WebService(endpointInterface = "com.putridparrot.core.HelloWorld", serviceName="HelloWorld")
    public class HelloWorldImpl implements HelloWorld {
        public String reply(@WebParam(name = "text") String text) {
            return "Hello " + text;
        }
    }
    
  • Finally paste the following into App.java
    package com.putridparrot.core;
    
    import javax.xml.ws.Endpoint;
    import java.io.IOException;
    
    public class App {
       public static void main( String[] args ) 
          throws IOException, InterruptedException  {
    
       HelloWorldImpl impl = new HelloWorldImpl();
       String address = "http://localhost:9000/HelloWorld";
       Endpoint.publish(address, impl);
    
       // hold the service open
       System.in.read();
    
       System.exit(0);
    }
    

As you’ve probably noticed, we’re not using Spring to create our webservice (the HelloWorldImpl) and thus there’s no spring configuration or the likes. This really is a bare bones implementation to get us started.

Now if you run this you should be able to use the URL http://localhost:9000/HelloWorld?wsdl to grab the definition meta data. Hence from a C# client you can use wsdl.exe or using Visual Studio’s Add Service reference to generate C# classes to use this service.

Alternate code to start the server

In the above we used Endpoint to create the server, the alternative is to use the JaxWsServerFactoryBean, here’s the code

JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
factory.setServiceClass(HelloWorld.class);
factory.setAddress("http://localhost:9000/HelloWorld");
factory.create();

Beanify it

Okay, so we’ve proved the service works at this point, but we really don’t want to hardcode the service like this, so it’s type to turn it into a Spring bean.

First off let’s add to our pom.xml file, you’ll need the following added to the properties element

<spring.version>3.0.5.RELEASE</spring.version>

and then the following dependencies need to be added

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot</artifactId>
      <version>1.5.2.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>1.5.2.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2.2</version>
    </dependency>

and we’re going to use Jetty as the HTTP server, so also add the following dependency

<dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-transports-http-jetty</artifactId>
   <version>${cxf.version}</version>
</dependency>

Obviously you’ll need to run mvn install to get the dependencies updated.

Next we need to create our beans file, within this we’ll actually create a server/endpoint which will be executed. So create a resources folder off of src/main in your project’s folder and add an XML file, mine’s named cxf.xml – within this file we can use one of either of these configurations

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-http.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml" />

    <jaxws:server id="helloWorld"  serviceClass="com.putridparrot.core.HelloWorld" address="http://localhost:9000/HelloWorld">
        <jaxws:serviceBean>
            <bean class="com.putridparrot.core.HelloWorldImpl"/>
        </jaxws:serviceBean>
    </jaxws:server>
</beans>

or using an Endpoint we can use the following

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-http.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml" />

 <jaxws:endpoint id="helloWorld" implementor="com.putridparrot.core.HelloWorldImpl"
                    address="http://localhost:9000/HelloWorld">
        <jaxws:features>
            <bean class="org.apache.cxf.feature.LoggingFeature"/>
        </jaxws:features>
    </jaxws:endpoint>
</beans>

You can change the address to address=”/HelloWorld” if you are using the default port etc. Otherwise to remove this will require looking into overriding Jetty’s port etc. (which is outside the scope of this post).

Finally we need to have the main method wire everything up, so within main we should have the following

new ClassPathXmlApplicationContext("cxf.xml");

// stop the server shutting down
System.in.read();
System.exit(0);

Adding data to WCF headers

As I’ve covered this subject using WSE3, now to look at adding an SSO token to a WCF header.

Configuration

In your App.config you’ve probably got something like

<system.serviceModel>
   <client configSource="Config\servicemodel-client-config.xml" />
   <bindings configSource="Config\servicemodel-bindings-config.xml" />
</system.serviceModel>

We’re going to add two more configuration files, as follows

<system.serviceModel>
   <client configSource="Config\servicemodel-client-config.xml" />
   <bindings configSource="Config\servicemodel-bindings-config.xml" />
   <!-- Additions -->
   <behaviors configSource="Config\servicemodel-behaviors-config.xml" />
   <extensions configSource="Config\servicemodel-extensions-config.xml" />
</system.serviceModel>

As the names suggest, these file will include the config for behaviors and extensions.

Let’s take a look at the servicemodel-behaviors-config.xml first

<?xml version="1.0" encoding="utf-8" ?>
<behaviors>
   <endpointBehaviors>
      <behavior name="serviceEndpointBehaviour">
         <ssoEndpointBehaviorExtension />
         <!-- insert any other behavior extensions here -->
      </behavior>
   </endpointBehaviors>
</behaviors>

Now we need to actually define what implements ssoEndpointBehaviorExtension. So in the servicemodel-extensions-config.xml configuration, we might have something like the following

<?xml version="1.0" encoding="utf-8" ?>
<extensions>
   <behaviorExtensions>
      <add name="ssoEndpointBehaviorExtension"
            type="SsoService.SsoEndpointBehaviorExtensionElement, SsoService"/>
      <!-- insert other extensions here -->
   </behaviorExtensions>
</extensions>

So as we can see, the ssoEndpointBehaviorExtension behavior is associated with the SsoService assembly and the type SsoEndpointBehaviorExtensionElement.

Implementing the behavior/extension

Unlike WSE3 we do not need to use an attribute to associate the extension with a service call.

Let’s start by looking at the SsoEndpointBehaviorExtensionElement behavior extension.

public class SsoEndpointBehaviorExtensionElement : BehaviorExtensionElement
{
   public override Type BehaviorType
   {
      get { return typeof(SsoEndpointBehavior); }
   }
   
   protected override object CreateBehavior()
   {
      return new SsoEndpointBehavior();
   }
}

The code above relates to the actual extension element, so really just creates the actual behavior when required. Here’s the SsoEndpointBehavior.

public class SsoEndpointBehavior : IEndpointBehavior
{
   public void AddBindingParameters(ServiceEndpoint endpoint, 
                 BindingParameterCollection bindingParameters)
   {
   }

   public void ApplyClientBehavior(ServiceEndpoint endpoint, 
                 ClientRuntime clientRuntime)
   {
      clientRuntime.MessageInspectors.Add(new SsoMessageInspector());
   }

   public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
                 EndpointDispatcher endpointDispatcher)
   {
   }

   public void Validate(ServiceEndpoint endpoint)
   {
   }
}

This code simply adds the inspector to the message inspector. Finally let’s look at the code that actual intercepts the send requests to add the SSO token to the header.

public class SsoMessageInspector : IClientMessageInspector
{
   public object BeforeSendRequest(ref Message request, IClientChannel channel)
   {
      request.Headers.Add(MessageHeader.CreateHeader("ssoToken", 
              String.Empty, 
              SsoManager.TokenString);
      return null;
   }

   public void AfterReceiveReply(ref Message reply, object correlationState)
   {
   }
}

In the above code, we create an addition to the message header, with the name “ssoToken” followed by any namespace and then the value we wish to store with the header item. In this case our SSO token.

Adding envelope headers to SOAP calls using WSE 3

This is a little old school as greenfield projects are more likely to use WCF or some other mechanism – but this blog is all about reminding myself how things work. In this case I’m going to look back at how WSE3 can be used to add data to the header of a SOAP envelope for web service calls.

WSE or Web Service Enhancements (from Microsoft) allows us to intercept SOAP messages and in the case of this post, add information to the SOAP message which can then be read by a service.

A likely scenario and one I will describe here is that we might wish to add a security token to the SOAP message which can be used by the service to ensure the user is authenticated.

Let’s get started

If you’ve not already got WSE3 on your machine then let’s get it, by either downloading WSE3 and then referencing the following assembly

Microsoft.Web.Services3

or use NuGet to add the WSE package by using

Install-Package Microsoft.Web.Services3

Configuration

Next we need to create some config. So add/edit your App.config to include the following

<configSections>
   <section name="microsoft.web.services3" 
        type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration, 
        Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, 
        PublicKeyToken=31bf3856ad364e35" />
</configSections>

<microsoft.web.services3>
   <policy fileName="wse3ClientPolicyCache.config" />
      <diagnostics>
         <trace enabled="false" 
                input="InputTrace.webinfo" 
                output="OutputTrace.webinfo" />
         <detailedErrors enabled="true" />
   </diagnostics>
</microsoft.web.services3>

Notice we’ve stated the policy filename is wse3ClientPolicyCache.config (obviously this can be named whatever you want). This file will contain the config which denotes what extensions are to be executed during a web service call.

So let’s look at my wse3ClientPolicyCache.config file

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
  <extensions>
    <extension name="ssoTokenAssertion" 
               type="SsoService.SsoTokenAssertion, SSOService"/>
  </extensions>
  <policy name="SsoPolicy">
    <ssoTokenAssertion />
    <requireActionHeader />
  </policy>
</policies>

Here we’ve added an extension named ssoTokenAssertion which is associated with a type in the standard form (type, assembly). We then create a policy which shows we’re expecting the policy to use to extension ssoTokenAssertion that we’ve just added. The requireActionHeader is documented here.

Changes to the web service code

So we’ve now created all the config to allow WSE to work but we now need to write some code.

We need to do a couple of things to allow our web service code (whether generated via wsdl.exe or hand coded) to work with WSE. Firstly need to derive our service from Microsoft.Web.Services3.WebServicesClientProtocol and secondly we need to adorn the web service with the Microsoft.Web.Services3.Policy attribute.

In our configuration we created a policy named SsoPolicy, this should be the string passed to the Policy attribute. Here’s an example web service

[Microsoft.Web.Services3.Policy("SsoPolicy")]
public partial class UserService : WebServicesClientProtocol
{
   // implementation
}

In the above I’ve removed the wsdl generated code for simplicity.

Implementing our “SsoService.SsoTokenAssertion, SsoService” type

So we’ve got the configuration which states that we’re supplying a type SsoTokenAssertion which will add data to the SOAP envelope header. Firstly we create a SecurityPolicyAssertion derived class.

public class SsoTokenAssertion : SecurityPolicyAssertion
{
   private const string SSO_TOKEN_ASSERTION = "ssoTokenAssertion";

   public override SoapFilter CreateClientInputFilter(FilterCreationContext context)
   {
      return null;
   }

   public override SoapFilter CreateClientOutputFilter(FilterCreationContext context)
   {
      return new SsoClientSendFilter(this);
   }

   public override SoapFilter CreateServiceInputFilter(FilterCreationContext context)
   {
      return null;
   }

   public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
   {
      return null;
   }

   public override void ReadXml(XmlReader reader, IDictionary<string, Type> extensions)
   {
      bool isEmpty = reader.IsEmptyElement;
      reader.ReadStartElement(SSO_TOKEN_ASSERTION);
      if (!isEmpty)
      {
         reader.ReadEndElement();
      }
   }

   public override IEnumerable<KeyValuePair<string, Type>> GetExtensions()
   {
      return new[] { new KeyValuePair<string, Type>(SSO_TOKEN_ASSERTION, GetType()) };
   }
}

In the above code, we’re only really doing a couple of things. The first is creating an output filter, this will add our SSO token to the SOAP envelope header on it’s way to the server, the ReadXml really just throws away any token that might be sent to our code – in this case we don’t care about a token in the header but we might for some other application.

public class SsoClientSendFilter : SendSecurityFilter
{
   protected const string SSO_HEADER_ELEMENT = "ssoToken";

   public SsoClientSendFilter(SecurityPolicyAssertion parentAssertion) :
      base(parentAssertion.ServiceActor, true)
   {
   }

   public override void SecureMessage(SoapEnvelope envelope, Security security)
   {
      string ssoTokenString = SsoManager.TokenString;

      if (String.IsNullOrEmpty(ssoTokenString))
      {
          throw new ApplicationException(
 	   "Could not generate an SSO token. Please ensure your user name exists within SSO and the password matches the one expected by SSO.");
      }

      XmlElement ssoTokenElement = envelope.CreateElement(SSO_HEADER_ELEMENT);
      ssoTokenElement.InnerText = ssoTokenString;
      envelope.Header.AppendChild(ssoTokenElement);
   }
}

The above code is probably fairly self-explanatory. The SsoManager class is a singleton which simply authenticates a user and creates a token string which we will attach to the envelope header, which we do by creating an XmlElement and adding it to the header.

Further Reading

Just came across Programming with WSE which has some interesting posts.