Creating a Java extension that consumes a web service
Certified

Creating a Java extension that consumes a web service
Certified

  

Permalink: www.outsystems.com/goto/web-service-java-extension



Edit: this post refers to an outdated approach at importing web references. The method detailed by Leonardo Fernandes below is prefered.
You can still try this method if the preferred one is not desirable for any particular reason.




--------------------------------------


This posts explains how to consume an external WSDL using a Java extension. This technique is usually needed when trying to use unsupported WSDLs.


Steps

  1. Get the necessary files
  2. Create/Edit the extension
  3. Change the build.xml file of the extension
  4. Generating the stubs
  5. Use the generated stub

Step 1 - Get the necessary files


In order to consume a web service in an extension we'll need some libraries. These are:

  • activation.jar
  • getopt.jar
  • javassist.jar
  • jaxb-api.jar
  • jaxb-impl.jar
  • jaxb-xjc.jar
  • jaxws-rt.jar
  • jaxws-tools.jar
  • jboss-xml-binding.jar
  • jbossall-client.jar
  • jbossws-common.jar
  • jbossws-framework.jar
  • jbossws-native-client.jar
  • jbossws-native-core.jar
  • jbossws-native-jaxrpc.jar
  • jbossws-native-jaxws-ext.jar
  • jbossws-native-jaxws.jar
  • jbossws-native-saaj.jar
  • jbossws-spi.jar
  • log4j.jar
  • mail.jar
  • stax-api.jar
  • stax-ex.jar
  • streambuffer.jar
  • wstx.jar

These files can be located inside the jboss. They can be found inside the client folder.


Step 2 - Create/Edit the extension

  • First open Integration Studio, then create a new extension or open an existing one if you want to change it.
  • Make sure the Application Server is J2EE or Both
  • Add the necessary actions that will be exposed to the Service Studio
  • Click Edit Source Code J2EE in the Integration Studio Toolbar
  • Right-click the J2EE folder in the left tree, select properties, and in the tab Libraries tab, click Add External JARs to add each of the above jar files.

Step 3 - Change the build.xml file of the extension


We need to change the build.xml file to add the libraries to the classpath, so the extension can be correctly compiled. In addition will add a new target to the build.xml file so we can easily generate the stubs that will call the web service.

  • Open the build.xml file in the Eclipse (use the left tree).
  • Add the following property "<property name="javaHome" location="xx"/> ", where xx is the folder where java is instaled.
  • Add the following property "<property name="wsConsumeLocation" location="xx"/>", where xx is the folder where the jar files reside.
  • In the section <path id="classpath"> add the following (this will add the necessary jar files to the classpath):

<fileset dir="${wsConsumeLocation}">

  <include name="*.jar" />

</fileset>

<fileset file="${javaHome}\lib\tools.jar" />

  • Next add the following target to the end of the file (after the target all). Change xx to the location of the wsdl file (can be an URL).

<target name="wsdl2java">

  <taskdef name="wsconsume" classname="org.jboss.wsf.spi.tools.ant.WSConsumeTask">

    <classpath>

      <path refid="classpath" />

    </classpath>

  </taskdef>

  <mkdir dir="${build}" />

  <wsconsume fork="true" verbose="true" destdir="${build}" sourcedestdir="${src}" keep="true" wsdl="xx" />

</target>


Step 4 - Generate the stubs

  • With the build.xml file open, in the right tree (Outline), right click the wsdl2java target, select Run As and them Ant Build. The stubs will be generated.

Step 5 - Use the generated stubs

  • To use the generated stubs, simply create an instance of the class xxSoap, where xx is the name of the webservice. For example:

  xxSoap stub = new xx().getxxSoap();

  • Now simply invoke the desired actions of the variable stub, corresponding to the desired web methods.
Hi. This is an update to the procedures for consuming web services in a Java extension. The procedure above is still valid, but it's coupled with jboss-ws, and adds too many dependencies to your extension.

The following method uses the wsimport tool, which is available with JDK 1.6. This tool can be used via command-line, but we will show you how to integrate it with your ant task, so you can refresh the web service client any time you want, without remembering command lines.

Step 1 - Why are you doing this?
Did you know that Service Studio can import web services easily? And that web services support is always being constantly improved by our development team?
So, before attempting to integrate with a web service through an extension, you should try in Service Studio. And if you can't do it in Service Studio - for any reason - then please make sure to contact our support team, so at least we are aware of your problem.

Step 2 - Create/edit extension
This step should be really simple, and is exactly the same as described by Rui on the above post. I won't repeat it here.
This example will be based on a web service to retrieve weather information. The WSDL is available at: http://www.webservicex.com/globalweather.asmx?wsdl
Create the extension with the name "Weather".

Step 3 - Change build.xml
Create a target called "refresh-weather-client" with the following code:
<target name="refresh-weather-client">
    <!-- Change the jdk.home path below to comply with your system -->
    <property name="jdk.home" value="/usr/java/latest" />
    <property name="wsdl" value="http://www.webservicex.com/globalweather.asmx?wsdl" />
    <delete dir="outsystems/nosweather/webservice" />
    <java classname="com.sun.tools.internal.ws.WsImport">
      <classpath>
        <fileset dir="${jdk.home}/jre/lib">
          <include name="*.jar" />
        </fileset>
        <fileset dir="${jdk.home}/lib">
          <include name="tools.jar" />
        </fileset>
      </classpath>
      <arg value="-keep" />
      <arg value="-s" />
      <arg value="." />
      <arg value="-p" />
      <arg value="outsystems.nosweather.webservice" />
      <arg value="-Xnocompile" />
      <arg value="${wsdl}" />
    </java>
  </target>
Note that you will need to modify at least the path of jdk.home property. This ant target will call the wsimport command with the arguments:
  • -keep: this will generate the .java source code for the web service client, instead of just the .class binaries
  • -s .: the place to put the generated .java source code
  • -p outsystems.nosweather.webservice: the package for the web service client
  • -Xnocompile: it will simply avoid compilation of .java source code, making it faster for complex web services
There's also an alternative method for using wsimport in ant files by a wsimport ant task, but it requires JAX-WS libraries. If you're interested, check this documentation: http://jax-ws.java.net/nonav/2.1.1/docs/wsimportant.html

Step 4 - Generate the stubs
This step should also be simple: run the above ant target. Right-click on it, then select Run As -> Ant Build.

Step 5 - Use the generated stubs
In your extension, you can use the classes you have just generated. You might also test it under Eclipse itself, by creating a main method in one of the classes and executing it:




Oops.. so this service is returning XML information that we'll need to parse in order to make something useful. Well.. the work for an integration is never done!
Thanks, this really helps!

And if the ws uses structures and/or lists, either as input or output? How do we match the structures created in step 2 with the ws structures?
See this wsdl as an example of a litle more complex ws.

Once again thanks!
Hi Eduardo,

You need to create your structures manually in the extension and the convert them in the code to the generated structures from wsdlimport.

But your wsdl looks very simple and it should import fine in service studio.

If it doesnt work at first, try save it to your disk and remove the httpget, httppost and soap12 bindings.

Regards,
João Rosado
João Rosado wrote:
Hi Eduardo,

You need to create your structures manually in the extension and the convert them in the code to the generated structures from wsdlimport.

But your wsdl looks very simple and it should import fine in service studio.

If it doesnt work at first, try save it to your disk and remove the httpget, httppost and soap12 bindings.

Regards,
João Rosado
 
 Hi, thanks for the reply. The wsdl is just an example, it imports fine in service studio. In my work i'm trying to consume a web service that, at least for now, doesn't import in service studio (i'm using the java version). Because my experience in these matters is almoast none i start with this simple wsdl.

Regards
Eduardo Luís
Anyone as any idea what can be the cause of the following error:

loader constraint violation: loader (instance of org/jboss/web/tomcat/service/WebCtxLoader$ENCLoader) previously initiated loading for a different type with name "javax/xml/rpc/ParameterMode"

This appens when a action from the extension is called, and only if the action consumes a web service, if its a regular action no error occurs.


Thanks
Eduardo Luís


Eduardo Luís wrote:
Anyone as any idea what can be the cause of the following error:

loader constraint violation: loader (instance of org/jboss/web/tomcat/service/WebCtxLoader$ENCLoader) previously initiated loading for a different type with name "javax/xml/rpc/ParameterMode"

This appens when a action from the extension is called, and only if the action consumes a web service, if its a regular action no error occurs.


Thanks
Eduardo Luís

 
 
 Hi Eduardo , have you included by any chance, some jars in the extension from axis or jaxws with deploy action "Copy to binaries directory".
They might be interfering with current jboss libs, and should not be included in the extension. 
I saw once a similar error when I used the eclipse wizard to consume the wsdl and those jars were included in the extension without me noticing.


EDIT: using the method that Leo suggests, it should not give you any problems related with that.

Hope this helps.

Regards, 
Luís Lopes


I'm using Leo method. Has far as i now no extra jars where included. 


Thanks for the help anyway.


Regards
Eduardo Luís
Ok, usually that error is associated with linkage errors, and with types that exist in multiple jars.
From the error, it says that the classloader already has loaded a javax/xml/rpc/ParameterMode, hence my hint that it could be originated from jars included in the extension.
Normally the troubleshoot involves inspecting webapp deployed jars for a javax.xml.rpc.ParameterMode.
From what I know that type is included in the jbossws-native-jaxrpc.jar that is included with Jboss, so it is not needed to included again along with the application.

If that problem is blocking your development I recommend you to contact support.

Regards,

Luís Lopes
some commandlinefu that might help 
in the App that is giving the problem, assuming it is called APP.war, it should exist in the server side a 

/opt/jboss-5.1.0.GA/server/outsystems/deploy/APP.war/WEB-INF/lib

if you execute in the linux shell something like:

for i in $(ls -1 *.jar); do jar -tf  $i | grep ParameterMode; done;

it should scan all jars to try to find if some has a ParameterMode in there.

Hope this can help.

Regards, 

Luís Lopes
Hi, thank you very much for you help!

Has far i cal tell the 
jbossws-native-jaxrpc.jar is not included in the extension. I've done a search in Eclipse for 'ParameterMode' and found nothing. The command line option return this:

[root@FAP-DEV02 lib]# for i in $(ls -1 *.jar); do jar -tf  $i | grep ParameterMode; done;
javax/xml/rpc/ParameterMode.class
[root@FAP-DEV02 lib]#



Once again, thank you very much.

Regards
Eduardo Luís
Hi Eduardo, 
in fact there's a jar that contains that type, and that is what is causing the problem.
unfortunately my command line only shows that the class exists somewhere , and don't show what jar includes it :), sorry my bad.
If you alter it slightly :
for i in $(ls -1 *.jar); do jar -tf  $i | grep ParameterMode && echo in this file $i ; done;
if should give you the name of the jar that contains the type.

Once you have the name of the jar that contains that type, you should be able to check where it comes from and why is it being deployed.
It should not be deployed since it conflicts with jboss internal libraries.

Hope this helps.
As I said before, If that problem is blocking your development I recommend you to contact support.

Regards, 

Luís Lopes
Hi all,

I'm updating the information on this thread to complement the Leonardo Fernandes reply.

This solution to consume a web service uses JAX-WS as implementation. There is common issue that we have seen with this kind of implementation caused by the amount of time it takes to create the web client object, making the normal usage of complex services pretty slow.

In the example extension from Leonardo this object is created in every call, losing a lot of time.

To overcome this problem there are 2 improvements that can be made:
   1 - Instead of creating the object in every call ( the "new GlobalWeather()" in the example) it can be cached in a static variable between usages.





   2 - Another possible problem is that the wsdl file (and all it's dependencies) can take too long to download, and when the service is created the JAX-WS implementation will always try to download it again! A solution to speedup that process is to place the files all the required files locally inside the jar.

So using the previous example from Leonardo, we can download the file manually from http://www.webservicex.com/globalweather.asmx?wsdl and place it inside the a \Source\J2EE\outsystems\nosweather\wsdl folder (remember to change to match the name in your extension, and the wsdl folder needs to be created.
On my example lets assume I saved it in a file named globalweather.wsdl
Then change the target in the build.xml file accordingly to add the highlighed part:

<target name="refresh-weather-client">
    <!-- Change the jdk.home path below to comply with your system -->
    <property name="jdk.home" value="/usr/java/latest" />
    <property name="wsdl" location="outsystems/nosweather/wsdl/globalweather.wsdl" />
    <delete dir="outsystems/nosweather/webservice" />
    <java classname="com.sun.tools.internal.ws.WsImport">
      <classpath>
        <fileset dir="${jdk.home}/jre/lib">
          <include name="*.jar" />
        </fileset>
        <fileset dir="${jdk.home}/lib">
          <include name="tools.jar" />
        </fileset>
      </classpath>
      <arg value="-keep" />
      <arg value="-s" />
      <arg value="." />
      <arg value="-p" />
      <arg value="outsystems.nosweather.webservice" />
      <arg value="-Xnocompile" />
      <arg value="-wsdllocation" />
      <arg value="../wsdl/globalweather.wsdl" />
      <arg value="${wsdl}" />
    </java>
  </target>
You will also need to tweak the "jar" target a bit in that build.xml file. Just search for it and add the highlighed part.

  <target name="jar" depends="compile">
    <jar destfile="${build}/outsystems.nosweather.jar">
      <fileset dir="${build}">
        <include name="**/*.class" />
      </fileset>
      <fileset dir="${src}">
          <include name="**/*.wsdl" />
          <include name="**/*.xsd" />
      </fileset>
    </jar>
  </target>


Please keep in mind that this is a simple example. Complex wsdl's will have ".xsd" dependency files. I recomend that in that case you either see if your service provider has a download available with all the files or use the "Export" option in a tool like SoapUI to do that job for your.

I'm attaching a new version of the example xif with these 2 changes.

Regards,
João Rosado

Also, there is another alternative library that is commonly used to consume web services, called Axis.
Axis an older and more problematic library that should only be used if it's really necessary.

I'm posting this because sometimes JAX-WS is not an option. Please try to avoid this unless:
  • Your service is RPC-Encoded (this type of web service is not supported by JAX-WS)
  • Your web service provider specifically says that you must use Axis to consume their web services

How to import a web service using the Axis libraries:
(Note: this process was tested with Axis 1.4 and JBoss 5)

  1 - Download the Axis 1.4 libraries from their website. Select 1.4 and then download the axis-bin-1_4.zip file

  2 - Unpack the zip file

  3 - Copy the files inside the "\lib" folder to the "\Source\J2EE\lib" folder inside your extension.

  4 - VERY IMPORTANT STEP: You need to include the files in your extension, but you need to set the deploy action of allmost all of them to Ignore. If you don't do this they will conflict with the files natively in jboss and your application will stop working!

The following files marked in blue need to be added as "Ignore", they are used to compile the extension, but since jboss already has versions of them natively they cannot be published:



Then only these 2 files (axis.jar and common.discovery-0.2.jar) should be set to "Copy to Binaries directory":



Like I said, this was tested with jboss 5. I'll test with jboss 7 later and post an update if I find any changes needed.


5 - To generate the proxies java files add this target to your build.xml:

    <path id="axis.classpath">
        <fileset dir="${lib}">
            <include name="**/*.jar" />
        </fileset>
    </path>
    
    <taskdef resource="axis-tasks.properties" classpathref="axis.classpath" />

    <target name="refresh-weather-axis-client">
        <property name="remote.wsdl" value="http://www.webservicex.com/globalweather.asmx?wsdl" />
        <axis-wsdl2java
          output="."
          testcase="false"
          verbose="true"
          url="${remote.wsdl}"
          all="true"
          timeout="200000">
        </axis-wsdl2java>
    </target>
Full documentation of the axis-wsdl2java target can be found in the Axis site. Change it as needed.

6 - Change the code to use the axis classes. Code is similar to the JAX-WS one, but you will need to seach for the Locator class of your service. Use the Ctrl+Shift+T search option in eclipse and seach for a class with name ending with "ServiceLocator".
 Then your initial code should be something like:

WeatherServiceLocator service = new WeatherServiceLocator();
WeatherPortType port = service.getWeatherPort();



Regards,
João Rosado
1. The Build.xml gets refreshed and the changes made to it were getting deleted. Is this expeted.
2. URL should get passed in as the parameter right, (I am thinking to define a site property to set the URL and pass that as parameter)
Hi Vasanth,

What changes are getting deleted from your build.xml file? (by the way we are talking about the build.xml inside the extension, right?)

As for the url, yes you will need to pass it as parameter if you want it to be configurable (like different between your environments).

Regards,
João Rosado
Sorry, the issue is no more, it is the build.xml inside the extension. Thank you :)
Are there automated way of creating the Structure in the Integration Studio (From WS Stub ), As the WSDL which i have contains roughly 8000 elements. If not readily avilable is it feasible to do.

Long path tool is the very good program for error, unlock solution.

Try it and solve your problem.

I used long path tool and I solve my error, unlock problem solution.