***** Apache Axis 1.4 Web Service Notes ***** Sample Project Directory Structure: ---------------------------------- + src + build + deployment + test + tomcat + bin + conf + logs + temp + webapps + axis + WEB-INF + lib - server-config.wsdd - web.xml + build-config + + tomcat + bin + conf - build.properties - build.properties - deploy.wsdd - undeploy.wsdd + test + vba-client + swing-client + webapps + axis + WEB-INF + lib - server-config.wsdd - web.xml build.xml Building Platform Independent Web Services: ------------------------------------------ 1. Avoid using platform specific keywords as method/field names (e.g. VB keywords, Java keywords, etc.) *** VB related words to avoid *** Keywords: As,Binary,ByRef,ByVal,Date,Else,Empty,Error,False,For,Friend,Get,Input,Is,Len,Let,Lock,Me,Mid,New,Next,Nothing,Null,On,Option,Optional,ParamArray,Print,Private,Property,Public,Resume,Seek,Set,Static,Step,String,Then,Time,To,True,WithEvent Statements: AppActivate,Beep,Call,ChDir,ChDrive,Close,Const,Date,Declare,Deftype,DeleteSetting,Dim,Do...Loop,End,Enum,Erase,Error,Event,Exit,FileCopy,For Each...Next,For...Next,Function,Get,GoSub...Return,GoTo,If...Then...Else,Implements,Input #,Kill,Let,Line Input #,Load,Lock, Unlock,LSet,Mid,MkDir,Name,On Error,On...GoSub, On...GoTo,Open,Option Base,Option Compare,Option Explicit,Option Private,Print #,Private,Property Get,Property Let,Property Set,Public,Put,RaiseEvent,Randomize,ReDim,Rem,Reset,Resume,RmDir,RSet,SavePicture,SaveSetting,Seek,Select Case,SendKeys,Set,SetAttr,Static,Stop,Sub,Time,Type,Unload,While...Wend,Width #,With,Write Blue Words: AddressOf,Alias,And,Any,As,Binary,Boolean,ByRef,Byte,ByVal,Call,Case,CBool,CByte,CCur,CDate,CDbl,CInt,CLng,Close,Const,CSng,CStr,Currency,CVar,CVErr,Date,Debug,Declare,DefBool,DefByte,DefCur,DefDate,DefDbl,DefInt,DefLng,DefObj,DefSng,DefStr,DefVar,Dim,Do,Double,Each,Else,End,Enum,Eqv,Erase,Error,Event,Exit,For,Friend,Function,Get,Get,Global,GoSub,GoTo,If,Imp,Implements,In,Input,Integer,Is,LBound,Len,Let,Lib,Like,Line,Lock,Long,Loop,LSet,Mod,Name,New,Next,Not,Nothing,Null,Object,On,Open,Option Base 1,Option Compare Binary,Option Compare Database,Option Compare Text,Option Explicit,Option Private Module,Optional,Or,Output,ParamArray,Preserve Source: http://www.ostrosoft.com/vb/get_vb_keywords.asp 2. Do not use WSDL "dateTime" in web service method arguments or fields. The reason is it get mapped to java.util.Calendar and the timezone will be based on the caller/server accordingly. This may be confusing to the user. Instead, consider using a custom "date" type or simply use a "string". Also, VBA seem to have problem with null dates. It seems that the Microsoft SOAP Toolkit does not understand xsi:nil="true" in the XML response from the web service. A possible reason for this is that a date is a primitive type so it cannot be null in VBA. 3. When possible return null instead of any empty array. The problem with an empty array is that using the VBA LBound() and UBound() will cause "Subscript out of range" error in VBA. To prevent this, a separate count/flag must be returned to indicate the size of the array. 4. Work with Java types that are known to be able to map to VB so that we can simply auto generate the WSDL file to save time and make development more maintainable. Instead we only need to maintain the "deploy.wsdd" file. 5. Web Service methods can throw Exceptions which will be mapped as SOAP "faults" and is raised as "Errors" in VB. e.g. VB Error handling: On Error GoTo ErrorHandling ... ErrorHandling: MsgBox ("#" & Err.Number & ": " & Err.description), vbCritical, "Error" 6. Use Ant to automate as much as possible. For example, use "axis-wsdl2java" Ant task to generate Java code from WSDL and "axis-java2wsdl" Ant task to generate WSDL from Java code. 7. Using login/logout approach (requires HTTP session) OR security token on each method call for security. Analysis is required to see which approach is more efficient for the web service being implemented. Detailed Development Steps: -------------------------- 1. Develop/Modify the Web Service (includes interface and implementation class that implements the interface, java.rmi.Remote, and javax.xml.rpc.server.ServiceLifecycle) 2. Regenerate/update the .wsdl file (if needed). This step is not needed if the client code can be generated from the dynamically generated WSDL file (e.g. http://localhost:7500/axis/services/ProjWebService?WSDL). Ideally, the dynamnic approach is better since it is a more maintainable approach. Note: It appears that Axis 1.4 will order all the fields of a class alphabetically. This means that if you add or remove fields from a class, the generated constructor signature will change. To make your code more flexible (and safe), consider using a no-arg constructor and use getter/setters instead. 3. Edit the "deploy.wsdd" XML file to add new methods to the "allowedMethods" parameter, add new "operation" entries for new methods, new "beanMapping" and "arrayMapping" entries (note Java Exceptions are specified as "beanMapping" entries with "fault" element in the "operation" entries). "typeMapping" are used for custom types that requires non-default serializer="org.apache.axis.encoding.ser.BeanSerializerFactory", deserializer="org.apache.axis.encoding.ser.BeanDeserializerFactory", and encodingStyle="http://schemas.xmlsoap.org/soap/encoding/". 4. Run the Axis server e.g. Tomcat w/ the following settings: Main classs: org.apache.catalina.startup.Bootstrap VM arguments: -Xmx512m -ea -Dcatalina.home="C:\tools\tomcat" -Djava.endorsed.dirs="C:\tools\tomcat\common\endorsed" -Dcatalina.base="C:\work\thinkui\project\workspace\projwebservice\build\tomcat" -Djava.io.tmpdir="C:\tools\tomcat\temp" 5. Deploy the web service using "deploy_ws.bat" file e.g. set AXISCLASSPATH=C:\tools\axis\webapps\axis\WEB-INF\lib\axis.jar;C:\tools\axis\webapps\axis\WEB-INF\lib\axis-ant.jar;C:\tools\axis\webapps\axis\WEB-INF\lib\commons-discovery-0.2.jar;C:\tools\axis\webapps\axis\WEB-INF\lib\commons-logging-1.0.4.jar;C:\tools\axis\webapps\axis\WEB-INF\lib\jaxrpc.jar;C:\tools\axis\webapps\axis\WEB-INF\lib\saaj.jar;C:\tools\axis\webapps\axis\WEB-INF\lib\wsdl4j-1.5.1.jar java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient -lhttp://localhost:7500/axis/services/AdminService C:\work\thinkui\project\workspace\projwebservice\build-config\deploy.wsdd When successfully completed, the corresponding "server-config.wsdd" file will be updated in the Axis server. Copy the updated "server-config.wsdd" file to webapps/axis/WEB-INF folder. 6. Test the new web service methods using the Eclipse "Web Services Explorer" or test from URL: http://localhost:7400/axis/services/ProjWebService?method= 7. Regenerate web service client code. For Java, use an Ant task (see build_axis_wsdl2java.xml file). For VBA, you need to perform the following: - Open Excel file and open the Visual Basic editor via Tools -> Macro -> Visual Basic editor (Alt-F11) - Save the "c_WSDL_URL" String from clsws_ProjWebService "Class Module" for later use. - Remove the previously generated "Class Modules" - if any (must be done one at a time!) - Select Tools -> Web Service References, select Web Service URL and enter WSDL URL. Press search, select the web service and press "Add" to regenerate the required Class Modules for accessing the web service - To run a macro, select the method name and press the "Play/Run" button. AspectJ Performance Profiling Notes: ----------------------------------- Use AspectJ to add performance profiling to your web service with minimal impact on your web service. - Install the AspectJ Eclipse 3.2 plug-in - Use a pointcut is to time how long each public web service methods. (e.g. track invocation count, total time, average, as well as number of exceptions encountered in each method) - Considering add special web service methods (intended to be used internally only) that will return the profile data (sorted by the longest average method time) as well as clear the data for subsequent test. - Add build targets to compile your web service. public aspect ProfileWebService { pointcut profileClass(): within(ProjWebService_Impl) || within(DataAccessObject); pointcut profileMethod(): profileClass() && execution(public * *(..)) && !execution(* init(..)); before (): profileMethod() { System.out.println("--> " + thisJoinPointStaticPart.getSignature()); } after(): profileMethod() { System.out.println("<-- " + thisJoinPointStaticPart.getSignature()); } Object around(): profileMethod() { long startTime = System.currentTimeMillis(); Object value = null; boolean exception = true; try { value = proceed(); exception = false; } finally { long endTime = System.currentTimeMillis(); long delta = endTime - startTime; String signature = thisJoinPointStaticPart.getSignature().toString(); synchronized(ProfileHelper.profileMap) { ProfileInfo pi = (ProfileInfo) ProfileHelper.profileMap.get(signature); if (pi == null) { pi = new ProfileInfo(signature); pi.firstCall = startTime; ProfileHelper.profileMap.put(signature, pi); } pi.lastCall = startTime; pi.totalTime += delta; pi.count++; if (exception) { pi.exceptions++; } pi.averageTime = (pi.totalTime / pi.count); System.out.println("[Time: " + startTime +"] "+ signature + " " + delta + " ms " + pi); } } return value; } }