Showing posts with label web services. Show all posts
Showing posts with label web services. Show all posts

Wednesday, April 16, 2008

Process for generating Netsuite java files, compiling stub classes, and creating jar file

If you've read other posts I've done about ColdFusion and Netsuite integration, you may recall that I solved an issue with MX7 refusing to compile all of the necessary stub files from the Netsuite WSDL by manually compiling them and creating a jar file. This used to be a huge hassle for me each time Netsuite released an upgrade. This time, however, it should be a ridiculously smooth process. I know the following steps will look intimidating if you are not familiar with Apache ANT or Apache Ivy, but trust me, they will make your life a lot easier.

Someone recently wrote me with some questions about this process, and I thought I would post this for everyone's benefit. Or at least I hope it's for your benefit and not your additional confusion. *grin*

Oh, and a couple of notes. I have not done any development in ColdFusion 8, so some of the steps and issues below may not apply. In fact, this entire process may not be necessary at all for all I know. Also I'm quite green when it comes to ANT and Ivy, so as with everything else I post here, if there is a more efficient way to any of these steps, I'd love to hear from you.

Assumptions for this process (modify as necessary for your environment):

  • [Netsuite WSDL version] = 2_6_0
  • [CF install path] = C:\CFusionMX7
  • You are using ColdFusion MX 7 (Notes for ColdFusion 8 will be given where necessary)
  • [java install path] = C:\j2sdk1.4.2_12
  • [major java version that your CF installation uses] = 1.4
  • [stub class target] = C:\netsuiteclasses\2_6_0
  • [Apache Axis version] = 1.2.1

Prerequisites:

  • You have Apache ANT 1.7.0 or higher installed and have all environment variables set per its installation documentation. (Download binaries and see documentation at http://ant.apache.org/)
  • You have Apache Ivy 2.0.0 beta 2 or higher installed and have all environment variables set per its installation documentation. (Download binaries and see documentation at http://ant.apache.org/ivy/)
  • A version of the java jdk compatible with the java runtime version of your ColdFusion installation.
  • NOTE: This item is only for ColdFusion MX7 and likely does not apply to ColdFusion 8. A modified version of the Apache Axis 1.2 jar file (axis.jar) (distributed by Netsuite to fix a login issue caused by how cookies are passed by Axis in the HTTP header) was copied into [CF install path]\lib, and the ColdFusion server was restarted afterward.

Generate and Compile Process:

1. Generate java class source code (windows dos window).

[java install path]\bin\java -cp [CF install path]\runtime\lib\wsdl2java.jar org.apache.axis.wsdl.WSDL2Java -v -O 120 -a -o [stub class target] https://webservices.netsuite.com/wsdl/[Netsuite WSDL version]/netsuite.wsdl

2. Create Apache ANT build file.

Create a file named build.xml in [stub class target] with the following contents (pay attention to bracketed areas that you will need to modify accordingly):

<?xml version="1.0" encoding="UTF-8"?>
<project name="netsuite_[Netsuite WSDL Version]" default="init" basedir="." xmlns:ivy="antlib:org.apache.ivy.ant">
<description>
Build file for Netsuite [Netsuite WSDL Version] WSDL.
</description>
<property name="classes.dir" location="classes" />
<property name="dist.dir" location="dist" />
<property name="dist.jarfile" value="netsuite_[Netsuite WSDL Version].jar" />

<target name="resolve" description="--> retrieve dependencies with ivy">
<ivy:retrieve pattern="${lib.dir}/[type]/[artifact]-[revision].[ext]" conf="runtime"/>
</target>

<target name="clean" description="Clean up output directories.">
<delete dir="${classes.dir}" />
</target>

<target name="compile" depends="-init" description="Compile all sources.">
<mkdir dir="${classes.dir}" />
<javac srcdir="." destdir="${classes.dir}" debug="true" deprecation="true">
<compilerarg line="-source [major java version that your CF installation uses]" /> <!—NOTE: The “-source” option may not be necessary for CF8 -->
<compilerarg line="-classpath [CF install path]\lib\axis.jar; [CF install path]\lib\jaxrpc.jar" />
</javac>
</target>

<target name="rebuild" depends="clean,compile" description="Cleanly compiles all sources." />

<target name="-init">
<!-- Create the time stamp. -->
<tstamp>
<format property="TODAY_US" pattern="dd MMM yyyy HH.mm" locale="en_US" />
</tstamp>
</target>

<target name="dist" depends="rebuild" description="Creates the binary distribution.">
<mkdir dir="${dist.dir}/${TODAY_US}" />
<jar basedir="${classes.dir}" destfile="${dist.dir}/${TODAY_US}/${dist.jarfile}" />
</target>

</project>

3. Create Apache Ivy config file.

Create a file named ivy.xml in [stub class target] with the following contents (pay attention to bracketed areas that you will need to modify accordingly):

<ivy-module version="2.0">
<info organisation="apache" module="hello-ivy"/>
<dependencies>
<dependency org="apache" name="axis" rev="[Apache Axis version]"/>
</dependencies>
</ivy-module>

4. Compile files and generate jar file.

Open a Command Prompt window and change directories to your [stub class target] directory and run the following command:

ant dist

You may see multiple warning messages throughout the compile process, but as long as you see a BUILD SUCCESSFUL message upon completion, there shouldn’t be any problems. Copy the generated jar file (the output of the ant command should indicate the filename and location of the jar file) to [CF install path]\lib and restart your ColdFusion Application Server service.

Friday, November 10, 2006

Preventing ColdFusion from recompiling web service stub classes

As I have been for the past few months, I'm currently working on an interface Netsuite via their web service. As posted here previously, it's brought up a few interesting challenges using ColdFusion. One annoyance was that following a ColdFusion service restart the next call to the Netsuite web service would take for-friggin-ever. I figured out it was because ColdFusion was refreshing and recompiling the stub Java classes generated from Netsuite's very deep and complex WSDL. Their WSDL results in over 500 distinct class files. From my local machine on which I was developing the code, it would take 5 to 10 minutes. When you're testing, screwing things up, and restarting ColdFusion regularly, this gets to be more than a little tedious.

I looked online to see if I could find any information about preventing ColdFusion from refreshing the stub classes, but no dice. I could only find information about how to force it to recompile. Not what I wanted. But perhaps someone out there knows of a server setting or some sort of parameter to prevent refreshing from the WSDL.

Now you might ask why I wouldn't want to make sure I have the latest and greatest from Netsuite. The problem is that for some unknown reason, ColdFusion refuses to generate and compile all of the necessary stub classes, so I can't rely on that process to get me everything I need. Perhaps the WSDL is too nested and complex? But that doesn't make sense because ColdFusion (I would presume) is using wsdl2java, which comes with Apache Axis. So why when I run wsdl2java from the command line does it successfully generate each and every java source file? I'm still scratching my head over that one. In any case, to ensure I had all necessary stub classes, I manually ran wsdl2java and compiled all of the resulting java files into class files from the command line, then whisked them away into a jar file that I dumped in the \CFusionMX7\lib directory.

Once I figured that the delay after the service restart was due to the stub classes being refreshed and ruled out preventing it with server settings or parameters, I realized I could simply avoid creating a ColdFusion web service object entirely by directly creating a java object from the stub classes. Unfortunately, the renders useless some of ColdFusion's handy functions like getSOAPRequest and getSOAPResponse.

Example:
<!--- Create a plain old coldfusion web service object (could also do a cfinvoke).---> 
<cfset ws = CreateObject("webservice", "https://webservices.netsuite.com/wsdl/v2_5_0/netsuite.wsdl")>
<!--- call some function from that web service --->
<cfset ws.MakeSomethingHappen()>
<!--- dump the entire soap request --->
<cfdump var="#getSOAPRequest(ws)#">
<!------ --- ----- ------ --------- ------------- --->
<!--- Create an equivalent java object directly from the service locator stub class (the class name "[web service name]ServiceLocator" and method "get[web service name]Port()" will be what to look for; i.e. NetsuiteServiceLocator.getNetsuitePort(), MyWebServiceNameServiceLocator.getMyWebServiceNamePort(), etc) --->
<cfset ws = CreateObject("java", "com.netsuite.webservices.platform_2_5.NetSuiteServiceLocator").getNetSuitePort()>
<!--- call the same function from that web service as the previous example --->
<cfset ws.MakeSomethingHappen()>
<!--- dump the entire soap request (it's a little messy, but you can always write your own udf that returns the same thing)... --->
<cfdump var="#ws._getService().getCall().getMessageContext().getRequestMessage().getSOAPEnvelope().toString()#">
<!--- ... or dump only the request body --->
<cfdump var="#ws._getService().getCall().getMessageContext().getRequestMessage().getSOAPBody().toString()#">