Social Icons

Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Wednesday, 30 April 2014

Creating Java Web Services Using JAX-WS, JAXB and Spring

I've recently been working a lot with Java web services, most of these were greenfield projects where we were able to choose the architecture. I decided to use JAX-WS to create the web services but was unsure initially of the best way to go about this. In general there are two approaches to writing web services (contract first or code first).

Contract first
Contract first requires a wsdl to be written first and then JAX-WS can be used to generate matching code. This approach has its place if you already have a wsdl but they're not the easiest of things to work with and maintenance quickly becomes messy.

Code first
Code first is much easier as you simply write the code using a handful of basic annotations and let JAX-WS generate the wsdl for you at run time. The downside to this approach is that you have to write the code before you have a wsdl or schema available. This may not be an issue but if you need to write a specification first then it's handy to have some kind of schema to define how the inputs and outputs are going to look.

Also, in some cases you may be able to express more in a schema than you can with just Java code. For instance you might want to set a restriction in the schema (like a string max length), or perhaps you want a particular nested structure of elements. You could do this yourself with JAXB annotations but it's easier to write a schema and generate the required classes.

Combined approach using JAXB
I asked this as a question on stackoverflow and one of the answers provided inspiration for a kind of best of both worlds approach. This has now been implemented for several different projects and overall it's been a pleasure to work with. The basic idea is that development follow something like the following process.
  • Write a basic schema that defines the request and response types (this can be included in specifications and is easier to maintain than a full wsdl)
  • Use JAXB/XJC to generate the request and response types
  • Write a JAX-WS endpoint using the generated types as inputs and outputs
  • Let JAX-WS generate the full wsdl at runtime

Spring integration
In addition to this I wanted to use spring for managing services, dependency injection and loading properties files. This requires setting up JAX-WS slightly differently so that spring can load the endpoints and inject dependencies. If you don't do this then you'll end up with a JAX-WS version of the endpoint with none of its dependencies injected while spring will have its own instance complete with injected dependencies but not handling any requests.

Maven build and dependencies
I'm using maven to manage the build and dependencies. You don't have to use maven but it really does make life much easier. These are the required dependencies you'll need in your pom.xml file.

<dependency>
         <groupId>com.sun.xml.ws</groupId>
         <artifactId>jaxws-rt</artifactId>
         <version>2.2.7</version>
       </dependency>

       <!-- Spring DI -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring-version}</version>
            <exclusions>
            <exclusion>
             <groupId>commons-logging</groupId>
             <artifactId>commons-logging</artifactId>
            </exclusion>
         </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <!-- JAX-WS/Spring integration -->
       <dependency>
   <groupId>org.jvnet.jax-ws-commons.spring</groupId>
   <artifactId>jaxws-spring</artifactId>
   <version>1.8</version>
            <exclusions>
            <exclusion>
             <groupId>org.springframework</groupId>
             <artifactId>spring</artifactId>
            </exclusion>
         </exclusions>
  </dependency>

In the build section of the pom you'll also need to configure the JAXB plugin to auto-generate code from your schemas. The way it's setup here it will look for any schema in the directory /src/main/resources/xsd and then generate code and put it in a source folder called /target/generated-sources/src/main/java. If you're using eclipse you'll want to right click this folder and take the option Build path &gt; Use as source folder. If you don't do this you'll still be able to run a build using maven but you'll probably see compile errors in eclipse.

<build>
        <plugins>
            <!-- Generate JAXB Java source files from an XSD file -->
            <plugin>
             <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxb2-maven-plugin</artifactId>
                <version>1.5</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>xjc</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!-- Don't set package name here as we want different packages for each schema.
                         Instead we set in each schema separately. -->
                    <!-- <packageName></packageName>  -->
                    <outputDirectory>${basedir}/target/generated-sources/src/main/java</outputDirectory>
                    <schemaDirectory>${basedir}/src/main/resources/xsd</schemaDirectory>
                </configuration>
            </plugin>
        </plugins>
  </build>

Web app config files
Next we need to add some configuration files to setup our web application. First is the web.xml deployment descriptor. Here we define the standard spring listener to load our spring configuration and we also setup a servlet to listen for our web service requests. Rather than use the JAX-WS servlet we've used a spring wrapper which will later allow us to use dependency injection in our endpoint classes.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <display-name>TestServices</display-name>

  <!-- Load spring configuration -->
  <context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/application-context.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- Servlet to handle all jax-ws requests -->
  <servlet>
    <servlet-name>jaxws-servlet</servlet-name>
    <servlet-class>com.sun.xml.ws.transport.http.servlet.WSSpringServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>jaxws-servlet</servlet-name>
    <url-pattern>/service/*</url-pattern>
  </servlet-mapping>

  <!-- There didn't ought to be any sessions created but it's good practice to define a
  timeout as it varies for different containers. -->
  <session-config>
    <session-timeout>40</session-timeout>
  </session-config>

</web-app>

The spring config is minimal in this basic example. It's just turning on classpath scanning for the package in our test project and enabling auto-wiring of scanned dependencies. We also need to setup which urls map to which endpoint classes but this has been moved to a separate file which we import at the bottom.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.2.xsd
       " >

    <context:component-scan base-package="com.testservices"/>
    <context:annotation-config/>

  <!-- Define the jaxws endpoint -->
  <import resource="jaxws-context.xml"/>

</beans>

Usually with JAX-WS you need a config file called sun-jaxws.xml where you define which urls map to which endpoints. In this case we're using a spring JAX-WS servlet so instead we add our mappings here. This simple mapping is saying all web service requests to /service/test1 will go to the spring bean with an ID of test1Services. We shall see this bean shortly.

<?xml version="1.0" encoding="UTF-8"?>
<beans  xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:ws="http://jax-ws.dev.java.net/spring/core"
  xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
   http://jax-ws.dev.java.net/spring/core
   http://jax-ws.dev.java.net/spring/core.xsd
   http://jax-ws.dev.java.net/spring/servlet
   http://jax-ws.dev.java.net/spring/servlet.xsd">  

  <!-- Define our jaxws endpoint (replaces sun-jaxws.xml) -->
    <wss:binding url="/service/test1">
        <wss:service>
            <ws:service bean="#test1Services" />
        </wss:service>
    </wss:binding>   

</beans>

Schema to define request/response types
In this simple example we could get away with one simple schema but in a real world example you'll probably end up with many. The way I've been organizing this is to have a base shared schema which defines common types. For example you might want all requests to include a username, password and environment and maybe the response should always have a boolean element to indicate success. Then you can write a schema for each endpoint defining the request and response types for all operations on that wsdl.

Here is the example base schema shared.xsd.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema  xmlns:xs="http://www.w3.org/2001/XMLSchema"
   xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
   jaxb:version="2.0"
   targetNamespace="http://shared.testservices.com/"
   xmlns:tns="http://shared.testservices.com/"
   elementFormDefault="qualified">

  <!-- Settings for the JAXB code generation -->
  <xs:annotation>
    <xs:appinfo>
      <!-- Set the package name for the generated classes -->
      <jaxb:schemaBindings>
        <jaxb:package name="com.testservices.generated.shared" />
      </jaxb:schemaBindings>
    </xs:appinfo>
  </xs:annotation>   

  <!-- Begin Types/Classes to be generated -->
  <xs:group name="baseRequest">
    <xs:sequence>
      <xs:element name="user" type="xs:string"/>
      <xs:element name="apikey" type="xs:string"/>
    </xs:sequence>
  </xs:group>    

  <xs:group name="baseResponse">
    <xs:sequence>
      <xs:element name="success" type="xs:boolean"/>
    </xs:sequence>
  </xs:group>   

</xs:schema>

This schema then imports the shared types and defines a simple request and response type for our example web service. Note that we can define which package the generated code belongs to by using the jaxb namespace extensions. There are a number of other customizations you can do like mapping xml types to Java types.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema  xmlns:xs="http://www.w3.org/2001/XMLSchema"
   xmlns:sh="http://shared.testservices.com/"
   xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
   jaxb:version="2.0"
   targetNamespace="http://test1.testservices.com/"
   xmlns:tns="http://test1.testservices.com/"
   elementFormDefault="qualified">

  <!-- Settings for the JAXB code generation -->
  <xs:annotation>
    <xs:appinfo>
      <!-- Set the package name for the generated classes -->
      <jaxb:schemaBindings>
        <jaxb:package name="com.testservices.generated.test1" />
      </jaxb:schemaBindings>
    </xs:appinfo>
  </xs:annotation>

  <xs:import namespace="http://shared.testservices.com/" schemaLocation="shared.xsd" />

  <xs:complexType name="test1Request">
    <xs:sequence>
      <xs:group ref="sh:baseRequest"/>
      <xs:element name="id" type="xs:int" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="test1Response">
    <xs:sequence>
      <xs:group ref="sh:baseResponse"/>
      <xs:element name="field1" type="xs:string" />
      <xs:element name="field2" type="xs:string" />
    </xs:sequence>
  </xs:complexType>

</xs:schema>

Finally add the endpoint interface/class
Having saved those schemas there should now be a generated class called Test1Request and another called Test1Response. We're now ready to start piecing things together. The final stage is to define the JAX-WS handler and add a web method that uses these classes as inputs and outputs.

@WebService
public interface Test1
{
 Test1Response test1(Test1Request request);
}

So we have a very simple interface which we annotate with the JAX-WS @WebService annotation. There is one method which uses our generated classes. When this code is deployed JAX-WS will do the legwork and generate the WSDL with this one method on it. We just need to implement this interface now.

@Component("test1Services")
@WebService(endpointInterface = "com.testservices.endpoint.Test1")
public class Test1Impl implements Test1
{

 ObjectFactory fact = new ObjectFactory();

 @Override
 public Test1Response test1(Test1Request request)
 {
  System.out.println("User: " + request.getUser());
  System.out.println("ID: " + request.getId());

  Test1Response response = fact.createTest1Response();

  response.setSuccess(true);
  response.setField1("Value 1");
  response.setField2("Value 2");

  return response;
 }

}

The implementation uses the JAXB object factory, which was generated for us, to create a response object. We then hardcode some values just to see if it works. In practice you'll be able to inject a spring service here and go off and do whatever business logic is required. The class has been annotated as a spring component called test1Services. It's important that this matches the bean name given in the spring config file for JAX-WS. The @WebService annotation names the interface that we've implemented.

You should now be able to fire up your test server and see this running. If you go to http://localhost:PORT/ProjectName/service/test1 you should see a page confirming the web service has been deployed with a link to the wsdl (just append ?wsdl).

Ben Thurley,
Senior Software Engineer at CoralTree Systems Ltd 

You can view Ben's wordpress blog, with more articles, here

Ben has worked for CoralTree for over 5 years. In that time, his expertise and knowledge in new coding techniques and methods have helped us develop even better solutions for our customers.

The views expressed in this article are not necessarily the views of CoralTree Systems Ltd.

Tuesday, 12 November 2013

Calling RPG on the AS400 from Java

A practical article on calling RPG on the AS400 from Java

RPG is the native language on the IBM as400 midrange server (aka iSeries, system i and now just "i"). In a recent project I had to find a way to call a number of RPG programs from a Java application. If you're in this situation then there are a few options available.
  • PCML (the subject of this article)
  • SQL Stored procedure
  • Integrated web services server (IWS)
Stored procedure
One possibility is to write a SQL stored procedure using RPG which could be called from Java using JDBC. This option may not be viable depending on the parameters your program needs. A stored procedure is good for returning result sets of records but you can only return one and you can't pass one in.

Integrated web services server (IWS)
If you want to quickly expose an RPG program as a web service then you might want to look at IWS. This is a quick way to get up and running but there are a number of limitations.
  • If you want multiple operations on the same wsdl you have to write them all as procedures in the same service program
  • If using a service program IWS only supports up to 7 parameters including both inputs and outputs
  • IWS only supports contract last development. In other words you have to write the code first to get the wsdl.
  • The generated wsdl has a number of duplicated elements which you have to manually remove to tidy the appearance.
  • If you change the parameters or operations you have to go through the whole wizard again on each machine you deploy to.
  • Arrays are fixed size in RPG so IWS always returns all elements, even if some are simply blanks.
PCML
The most flexible method is Program Call Markup Language (PCML). This is an API that IBM provided for just this scenario. PCML is an XML language for defining the parameter list for an RPG program. This can then be used from a Java application.

Generating the PCML
You could write the pcml file by hand but a better way is to get the RPG compiler to generate it for you. First lets write a simple RPG program that we want to call.

1:  D CONVTEMP    PR         Extpgm('CONVTEMP')   
2:  D iCelsius           9 3 Const            
3:  D oFahrenheit          9 3            
4:  D CONVTEMP    PI                     
5:  D iCelsius           9 3 Const            
6:  D oFahrenheit          9 3            
7:   /free                            
8:    // We could get an API to return whatever we like here  
9:    oFahrenheit = ((iCelsius * 9) / 5) + 32;         
10:    Return;                          
11:   /end-free                          

Yes it's the cliche web service example to convert temperature from Celsius into Fahrenheit. It's a good one to start with though because it is has both an input and an output but is still fairly simple. Note that the input parameter has been defined as a const, this is significant when we generate the PCML.

To generate the PCML from here you need to prompt compile and set the following options. Set PGMINFO to *PCML and INFOSTMF to a path on the ifs where you want your generated file to go e.g. /mylib/CONVTEMP.pcml. Doing so gives the following PCML.

1:  <pcml version="4.0">  
2:    <program name="CONVTEMP" path="/QSYS.LIB/PCMLTEST.LIB/CONVTEMP.PGM">  
3:     <data name="ICELSIUS" type="packed" length="9" precision="3" usage="input" />  
4:     <data name="OFAHRENHEIT" type="packed" length="9" precision="3" usage="inputoutput" />  
5:    </program>  
6:  </pcml>                         

We now have an XML file that describes how to call this program. Notice that the iCelsius parameter has been set to input but oFahrenheit is inputoutput. This is a result of setting iCelsius to a const parameter. When making a PCML call you must set a value for all input parameters. The default is inputoutput which can go both ways but is inconvenient if you don't have an input value to set. Unfortunately there's no language feature in RPG to set a parameter to output only so you have to adjust these manually.

1:     <data name="OFAHRENHEIT" type="packed" length="9" precision="3" usage="output" />                        

Java dependencies
To make a PCML program call you just need the jt400 jar on your classpath. You can either use the IBM version that comes bundled with the AS400 or the open source JTOpen version.

If you use maven then you can simply declare it as a dependency like this.
1:  <dependency>  
2:          <groupId>net.sf.jt400</groupId>  
3:          <artifactId>jt400</artifactId>  
4:          <version>6.7</version>  
5:  </dependency>                     

This works fine but sadly the JTOpen developers stopped publishing to maven central at version 6.7 (current version is 7.10 at time of writing).

Calling the program
This is everything you need to make the program call. The Java class below is a simple test that opens a connection, calls the program and returns the result.
1:  import java.math.BigDecimal;  
2:  import com.ibm.as400.access.AS400;  
3:  import com.ibm.as400.data.PcmlException;  
4:  import com.ibm.as400.data.ProgramCallDocument;  
5:  public class ConvertTemperature  
6:  {  
7:          private AS400  as400;  
8:          public ConvertTemperature()  
9:          {  
10:                  as400 = new AS400("SYSTEM", "USERNAME", "PASSWORD");  
11:          }  
12:          public BigDecimal celsiusToFahrenheit(BigDecimal celsius)  
13:          {  
14:                  BigDecimal fahrenheit = null;  
15:                  try  
16:                  {  
17:                          ProgramCallDocument pcml = new ProgramCallDocument(as400, "CONVTEMP");  
18:                          pcml.setValue("CONVTEMP.ICELSIUS", celsius);  
19:                          boolean rc = pcml.callProgram("CONVTEMP");  
20:                          if(rc)  
21:                          {  
22:                                  fahrenheit = (BigDecimal) pcml.getValue("CONVTEMP.OFAHRENHEIT");  
23:                          }  
24:                  }  
25:                  catch(PcmlException e)  
26:                  {  
27:                          e.printStackTrace();  
28:                  }  
29:                  return fahrenheit;  
30:          }  
31:          public static void main(String[] args)  
32:          {  
33:                  ConvertTemperature ct = new ConvertTemperature();  
34:                  ct.celsiusToFahrenheit(new BigDecimal(25.2));  
35:          }  
36:  }  
The second parameter to the ProgramCallDocument constructor is the path on the classpath to the PCML xml document. I created a file called CONVTEMP.pcml and put it in the src/main/resources folder. To keep things simple this is the root classpath folder, the .pcml suffix is not required as it is implied.

The PCML API will automatically handle converting Java types to AS400 types and back again. In this example the packed decimal from the AS400 becomes a BigDecimal in Java.

This is obviously a basic example that works as a proof of concept but there are a few additions worth mentioning if you want to use this in a production environment.

Adding connection pooling
Each time you create an AS400 object you're opening a physical connection to the AS400. Each new connection creates a new job on the AS400. It's obviously a bit wasteful to then throw this away and start with a fresh connection on the next call. A much better solution is to create a connection pool.

First you need to create the connection pool object. This code should live in it's own class so the pool can be shared by different parts of the application. You could also load a properties file from the classpath to set the connection pool properties.
1:  AS400ConnectionPool pool = new AS400ConnectionPool();  
Now each time you want a connection you simply ask the pool. If no connections exist then one will be created.
1:  AS400 as400 = pool.getConnection("SYSTEM", "USERNAME", "PASSWORD");  
Remember to always return the connection back to the pool once you're finished with it. This should be done in the finally section of the try/catch block to ensure the connection is returned if an exception is thrown.
1:  pool.returnConnectionToPool(as400);  
Setting a library list
The PCML file generated had a fixed path to a specific library. In practice you may find the program exists in different libraries and you want to use the one at the top of the library list. To do this we must first change the PCML file to not hardcode the library.

Change this:
1:  <program name="CONVTEMP" path="/QSYS.LIB/PCMLTEST.LIB/CONVTEMP.PGM">  
To this:
1:  <program name="CONVTEMP" path="/QSYS.LIB/%LIBL%.LIB/CONVTEMP.PGM">  
1:  import com.ibm.as400.access.AS400;  
2:  import com.ibm.as400.access.AS400Message;  
3:  import com.ibm.as400.access.CommandCall;  
4:  import com.ibm.as400.access.ConnectionListener;  
5:  import com.ibm.as400.access.ConnectionPoolEvent;  
6:  import com.ibm.as400.access.ConnectionPoolListener;  
7:  public class AS400ConnectionPoolListener implements ConnectionPoolListener  
8:  {  
9:    @Override  
10:    public void connectionCreated(ConnectionPoolEvent event)  
11:          {  
12:                  AS400 as400 = (AS400) event.getSource();  
13:                  CommandCall command = new CommandCall(as400);  
14:                  try  
15:                  {  
16:        String liblCommand = "CHGLIBL(QTEMP PCMLTEST QGPL)";  
17:                          if(command.run(liblCommand) != true)  
18:                          {  
19:                                  // Show the messages (returned whether or not there was an  
20:                                  // error.)  
21:                                  AS400Message[] messagelist = command.getMessageList();  
22:                                  for(int count = 0; count < messagelist.length; count++)  
23:                                  {  
24:                                          // Show each message.  
25:                                          System.out.println("System message: " + messagelist[count].getText());  
26:                                  }  
27:                          }  
28:      }  
29:          @Override  
30:          public void connectionExpired(ConnectionPoolEvent event)  
31:          {  
32:                  // Not currently overriden  
33:          }  
34:          @Override  
35:          public void connectionPoolClosed(ConnectionPoolEvent event)  
36:          {  
37:                  // Not currently overriden  
38:          }  
39:          @Override  
40:          public void connectionReleased(ConnectionPoolEvent event)  
41:          {  
42:                  // Not currently overriden  
43:          }  
44:          @Override  
45:          public void connectionReturned(ConnectionPoolEvent event)  
46:          {  
47:                  // Not currently overriden  
48:          }  
49:          @Override  
50:          public void maintenanceThreadRun(ConnectionPoolEvent event)  
51:          {  
52:                  // Not currently overriden  
53:          }  
54:  }  
Finally the event listener needs to be registered as an observer of the connection pool.
1:  pool.addConnectionPoolListener(new AS400ConnectionPoolListener());  
Summary
This is a basic example that shows how to call an RPG program from Java. To brush this up a bit for production you only really need a few classes to wrap the connection pool and loading of properties. This would allow you to set the library list on different servers with a simple properties file. I would use spring to load the properties and register a bean that holds the connection pool. If you have to support multiple environments then you could set the library list each time you get a connection. Alternatively it might be more efficient to pass the environment to the RPG program and handle it on the AS400.

Ben Thurley,
Senior Software Engineer at CoralTree Systems Ltd

You can view Ben's wordpress blog, with more articles, here

Ben has worked for CoralTree for over 5 years. In that time, his expertise and knowledge in new coding techniques and methods have helped us develop even better solutions for our customers.

The views expressed in this article are not necessarily the views of CoralTree Systems Ltd.