Thursday Aug 17, 2006

RESTful Web Services with JAX-WS

Here is my article on RESTful Web Services and some best practices to consider when developing them with JAX-WS.

Wednesday Aug 16, 2006

JAXWS 2.0.1 Performance Improvements

JAX-WS 2.0 shipped as an FCS version before JavaOne (2006/05/09). JAXWS 2.0.1 is a overhaul of JAXWS 2.0 RI for improved performance and better pluggability to allow other technologies like WSIT to be layered on top. It still conforms to the same JSR spec. The team has spent a lot of cycles trying to optimize JAX-WS performance and the results can be seen in the graph below.
Between JAX-RPC and JAX-WS 2.0.1 (ie the rearchitected implementation), performance has impoved between approximately 70% depending on the test cases.
The graph below shows a comparison of JAX-RPC vs JAXWS 2.0 and JAXWS 2.0 vs JAXWS 2.0.1


JAXWS 2.0 vs JAX-WS 2.0.1

JAX-RPC 1.1 vs JAX-WS 2.0.1

JAX-WS 2.0 was a rewrite from ground up of JAX-RPC and included a lot of new features (like integration with JAXB 2.0) which contributed to a slight regression in performance. Performance has been at the center of the implementation rearchitecture.Some enhancements include

  • Revisiting some of the key abstractions

    • For example. avioded using StAX and going straight from JAXB to OutputStream. This reduces the number of layers and avoids the escaping, character encoding and namespace management done by StAX.

    • Better handling of properties Prior to JAXWS 2.0.1 a HashMap was used to hold properties that were all pre-evaluated and in fact read more often than written.In JAX-WS 2.0.1 a distributed property set with lazy evaluation is used.

  • Collapsing the stack where ever possible

    • For example, initially a byte image of the whole message was kept around and a DOM built for the whole message to access headers. This inadvertently led to XML being parsed twice.

      To avoid this JAXWS 2.0.1 uses a stream buffer which records infoset to necklace-like data structure and allows it to be replayed later. It's not randomly accessible though.

      It supports various XML API's. Eg reads via XMLStreamReader and writes via XMLStreamWriter; or fire SAX events to ContentHandler.

  • Reducing the number of special purpose interfaces.

    • For example no more custom hooks for JavaEE

    • Better handling of endpointaddress property. In JAXWS 2.0 implementation this was stored in a string and then converted to a new URL() everytime. In the 2.0.1 implmentation its stored as a URL in the distributed property set and a URI is created for storing proxy information which is used by the JDK's URL.openStream()

Saturday Aug 12, 2006

SWE 645 @ George Mason University

I was pleased to find out that another graduate level course is using my book on Web Services as a text book.
SWE 645 at George Mason university.

Saturday Jul 29, 2006

Tango performance

In a recent exercise in tracking performance with WSIT (aka Tango), our project to enable interoperability between the Java platform and Windows Communication Foundation (WCF), we noticed a significant regression in JAX-WS performance. Tango builds on top of JAX-WS with additional pipes.The rearchitected branch of JAX-WS has a notion of pipes - following the pipe and filter architectural style. Turns out a chunk of the regression was originating from one of the pipes creating a JAXB context for every request, rather than caching it. Kohsuke sent out an email to the Tango dev mailing list, that can be found here. These actually apply to all developers in general using JAX-WS and JAXB. Context creation and factory lookups are expensive operations and shouldnt be repeated , especially for every request !

Monday Jul 24, 2006

DWR and Web Services

JAX-WS services can be easily accessed via a browser using AJAX technologies like Direct Web Remoting(DWR) to enable client access. The PurchaseOrder web service example discussed previously can easily be accessed from web pages with some simple JavaScript. Simply wrap the service proxy in a JavaBean,include the DWR Jar and configuration file in the WAR and access the JavaBean object in the JavaScript. Details on how to conifgure DWR can be found in the article here For our example the web page looks like

<script type='text/javascript' src='/docliteralfromwsdl-war/dwr/interface/POServiceDAO.js'></script>
<script type='text/javascript' src='/docliteralfromwsdl-war/dwr/engine.js'></script>
<script type='text/javascript' src='/docliteralfromwsdl-war/dwr/util.js'></script>
function createPO() {
function showPO(order){
document.getElementById("field1").innerHTML = DWRUtil.toDescriptiveString(order, 2);
function sendPO(order){
function showStatus(status) {
document.getElementById("field2").innerHTML = status.timestamp ;
document.getElementById("field3").innerHTML = status.orderid ;

The full war modified from the previous example fan be downloaded here
The advantages of DWR in particualr is that the proxy is located on the server and is serialized via JavaScript to the client. So architecturally you can have services that are located in the DMZ or even backends that are not exposd to clients directly. The other advantage is performance, when the cilent proxy and service are colocated you can avoid the serialization over the wire and use the JAX-WS local transport.
Ahh the possibilities !!

Wednesday Jul 19, 2006

Accelerating Java XML digital signature performance

If you are considering using XML Digital signatures in real world application, a recommened article for reading published by our group can be found here

Sunday Jul 16, 2006

Accessing JAX-WS endpoints with Java WebStart

Java Web Start is a software distribution technology and can be used to distribute JAX-WS clients. Essentially one writes and deploys a JNLP file on the server. The example I will use is the one described in a previous entry here. You still need to sign the jar files to grant them security permissions and Java Web Start also works on a sandbox model. Here is the except of the JNLP file. Note that you can use either the applet-desc or application-desc tag depending on your code. We use the applet-desc since our previous example was an Applet jar. Also you dont need the JAX-WS runtime on the client if you use Java SE 6 or Mustang and set <j2se version="1.6+" />

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://localhost:8080" href="webstart.jnlp">
<title>JAX-WS WebStart Demo</title>
<vendor>Sun Microsystems</vendor>
<homepage href="http://localhost:8080" />
<description>A Java Webstart test</description>
<j2se version="1.5+" />
<jar href="SignedApplet.jar" />
<jar href="sjavaee.jar" />
<jar href="sappserv-ws.jar" />
<all-permissions />
<applet-desc main-class="JAXWSApplet" name="JAXWSApplet" width="640" height="256"/>
<param name="endpointURL" value="http://localhost:8080/docliteralfromwsdl-war/jaxws"/>

Thursday Jul 13, 2006

Accessing JAX-WS endpoints from Applets

Though the usecase for such an application is rare, some developers want to access JAX-WS web services via Applets - this is indeed possible. To demonstrate the steps, lets use the endpoint based on the "Using XML in the SOAP body" strategy described here. Assuming you have the endpoint deployed successfully, modify the client to work as an Applet. For our example we will have a simple TextArea and Button to make the web service call and display the results.The source for the Applet can be found in file To get this Applet running a few basic steps need take place

a) Package the Applet and artifacts generated by the wsimport in a Jar file

b) Make the JAX-WS runtime available to the client. If you're using Java SE 6.0 (Mustang) and the browser plugin for that, then you should be all set. However if you're using J2SE 1.5 or an older version of Java then the JAX-WS Jars need to be made available to the browser. You can make these available in one of two ways. i) Get the JAX-WS distribution from and get the jars from the lib directory or ii) If you're using JAX-WS with Glassfish then locate the appserv-ws.jar and javaee.jar files in the lib directory.

c) Sign all the necessary JAR files using keytool and the jarsigner utility. This is a two step process that involves key generation and Jar file signing. A good technical article detailing this can be found here
keytool -genkey -alias signFiles -keystore mystore -keypass mykeypass -dname cn=Sun -storepass mystorepass

jarsigner -keystore mystore -storepass mystorepass -keypass mykeypass -signedjar SignedApplet.jar JAXWSApplet.jar signFiles

d) Place the signed JAR files and the HTML page with the Applet tag on the web server. In Glassfish, simply place all these contents in the glassfish\\docroot directory

Run the above example using targets in the following order to first build deploy and test the endpoint using a stand alone client ant create-war deploy-war run-wsdl-client. Then run the target sign-jaxws-ri which packages the Applet , signs the applet and signs the Glassfish Jars, plaing them in the build/signedjars directory along with an HTML file. Place the contents of this directory on your web server.Access the web page through the browser or appletviewer and you should see a result similar to the screen below when the button is pressed.

Tuesday Jun 06, 2006

JAX-WS and the upcoming Apocalypse

I ve often wondered what it takes and how one goes about becomes a strategy analyst, sitting in Think Tanks and being in the business of predicting the past. I came across Richard Monson-Haefel's blog critiqing how bad JAX-WS was and then subsequently eating his own words. As pointed out by James,an analyst actually eating his own words and somewhat retracting in position, may actually be a sign that the rapture is coming !

Sunday Jun 04, 2006

Real World Web Services Performance on CoolThreads

Out of the box scalability for the SunFire T1000 and T2000 boxes (CoolThreads technology)Glassfish using an internally developed macro benchmark that stresses all of the JWSDP API's (JAX-WS, JAX-RPC,JAXB, StAX, XWSS etc) and simulates a real world web services application middle tier application.
Scalability with some tunings on the JVM and the application server such as acceptor threads > number of users

Tuesday May 23, 2006

Performance for RESTful Web Services with JAX-WS

RESTful web services with JAX-WS show some interesting performance characteristics. I ran some tests with WSTest/Japex. The first graphic shows a comparison of a few different method calls using a RESTful endpoint and a WSDL based SOAP endpoint - both doing the same thing i.e accepting XML, Unmarshalling it with JAXB, doing something and Marshalling results back using JAXB. The client does no work in both cases and uses POST operations. As seen, the performance is more or less the same in the firt tests but tends to do better for the non-rest cases for the medium and larger paylods. This is probably more because of the tight integration in the non-rest case between JAX-WS and JAXB with optimized code paths rather than anything else.
In another comparison for the same endpoints (RESTful and WSDL based) with a filp on the client for an HTTP GET/POST each, shows how for the POST operation the performance is slightly better for the REST endpoint and significantly better for the GET operation. This is because the test sends the same XML request data and can leverage the HTTP caching framework provided by the server for GET operations. Glassfish servlet/jsp caching details can be found here but with no config the default is 30sec, enough for our test.
So how do endpoints compare when the caching framework is not leveraged (request data is randomized) ? RESTful endpoints perform slightly better but as expected there is almost no difference in GET/POST operations for the same RESTful endpoint.

Bottom line - if you're building RESTful web services (and you understand the tradeoff's of doing so in the first place) GET operations should be your first choice.

Tuesday May 16, 2006

Some J1 2006 sessions

Marc Hadley presented an interesting session today at JavaOne 2006 on RESTful web services. His deck can be found here

Another cool session was on Groovy presented by Rod Cope. Details here. The demo at the end was interesting, though not really practical- it did get the geeks in the audience going aahhh.Always a good sign for a presentation.

Slides for our Web Services Performance session can be found here BOF-2593
and for the technical session TS-9263 on Web Services can be found TS-9263

Monday Apr 24, 2006

Path_info showing up as null in Glassfish JAX-WS

I was trying to deploy some JAX-WS endpoints in Glassfish and for some reason this was always returning a null.
String path = (String)mc.get(MessageContext.PATH_INFO); Turns out that the url-pattern must terminate in a \*
The /jaxws/\* was the key. When the url-pattern was just /jaxws the endpoint kept showing up as a HTTP-404 error. Once I added the \* in the web.xml - voila !
Will eyeball the jax-ws code to figureout why it does this when I get some time. For now the \* fix works.

Hudson build tool

I recently got an overview of Hudson from Kohsuke . Its the coolest build tool I ve seen. Started out as his pet projects and is now used for building Glassfish. Hudson includes the ability to plug in multiple computers and share time/offload work into a cluster - and its all open source.

Sunday Apr 09, 2006

Server GC in CLR

The .NET CLR has a tonne of counters that allow you to get detailed information about what the CLR is doing with memory, Threading, garbage collection etc. Details in the MSDN guide at There are some other things that arent that well documented. For example the CLR's GC has two modes: Server GC and WorkStation GC as alluded to in this MSDN article.The default is the workstation gc, to enable the server the app.config file should contain something like
 <gcServer enabled=“true“ />
Also the GC can run concurrently (somewhat similar to parallelGC in Java) and is turned "on" by default. This is suitable for heavy user interaction based apps.To switch it off the configuration would need to be changed again to
 <gcConcurrent enabled="false"/>
Also I came across a tip to use the SoS (Son of Strike) debugger in Visual studio for debugging apps that are a mix of managed/unmanaged code. To do this enable “unmanaged code” debugging option in Project properties-Debug and set a breakpoint in the code. When hit open the “immediate window” and load SOS debugger extension through the “.load sos” command. Here is a detailed article on using SoS

Saturday Apr 08, 2006

CLR Native JIT'ing

As I mentioned earlier, the NGEN tool in the CLR can be used to force the JIT to compile the IL to native format. You can also use it directly on an exe. For example
C:\\TEMP\\TestApp\\TestApp\\bin\\Release>ngen install TestApp.exe
Microsoft (R) CLR Native Image Generator - Version 2.0.50727.42
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
Installing assembly C:\\TEMP\\TestApp\\TestApp\\bin\\Release\\TestApp.exe
Compiling 1 assembly:
    Compiling assembly C:\\TEMP\\TestApp\\TestApp\\bin\\Release\\TestApp.exe ...
TestApp, Version=, Culture=neutral, PublicKeyToken=null
This results in the native image of the executable being generated in the Native assembly cache and the file can be inspected here. (You need to do this from a DOS prompt as this dir may not show up in explorer)
Directory of C:\\WINNT\\assembly\\NativeImages_v2.0.50727_32\\TestApp\\3d175dbade97d648b002a9881f995ca8
04/06/2006  08:12 PM              .
04/06/2006  08:12 PM              ..
04/06/2006  08:12 PM            13,824
               1 File(s)         13,824 bytes
               2 Dir(s)     774,688,768 bytes free
To verify that the native image is indeed being used at runtime, you can use the Assembly Binding Log Viewer (Fuslogvw.exe) tool. Fire it up, change the settings and log location, start your app and hit refresh. Something like this will show up verifying that the native image is being used.
The general guideline seems to be that one should test out the performance for both native/non native images. Just because its native doesnt really mean it will perform better but you may be able to reduce the memory footprint,and share memory pages across applications. Theres some detail around this in an April 2005 MSDN mag article here.

Wednesday Mar 29, 2006

Oracle Spatial Database

I ve been using some of Oracle 10g's new Spatial features and their Java Spatial API lately. The flexibility and features it provides for GIS type applications simply rocks. For example you can create a table and have a colum that is of type SDO_GEOMETRY and store the coordiates in terms of X,Y (like longitude and latitude) or other Geometric/GML data. The API allows you to then query that Spatially indexed colum in a variety of ways. For example you can use the JDBC API to query
 slect ... where sdo_filter (mycolumname ,sdo_geometry(2003,null,null,sdo_elem_info_array(1,1003,3),sdo_ordinate_array (0,0,600,800))) = 'TRUE' 
returns all the points as oracle.spatial.geometry.JGeometry objects that are in the MBR (mean bounding rectangle) from 0,0 to 600,800 ! With the set of coordinates in hand you can link them up and draw a route similar to what you see in Google or mapquest when you query for driving directions. Theres a bunch of other operators that allow you to insert/query a variety of geometic shapes for range and nearest neighbour information. Pretty cool !

Friday Mar 24, 2006

C# and SubMaps

It took me a while to figure out why my C# code wasnt working as expected today. After much debugging it turned out that C# doesnt have a clean SubMap implementation, or least one that I could find. What I was assuming will work wasnt quite cutting it.In Java you can easily get a subMap using string keys based on a regular expression magically. For example the code, Prints out something like...
TreeMap  sortedMap = new TreeMap ();
for(int i = 0;i<10;i++)
    sortedMap.put("SomeKey_"+i, "SomeValue");
String low = "SomeKey_3", high = "SomeKey~";

{SomeKey_0=SomeValue, SomeKey_1=SomeValue, SomeKey_2=SomeValue, SomeKey_3=SomeValue, SomeKey_4=SomeValue,
 SomeKey_5=SomeValue, SomeKey_6=SomeValue, SomeKey_7=SomeValue, SomeKey_8=SomeValue, SomeKey_9=SomeValue}
{SomeKey_3=SomeValue, SomeKey_4=SomeValue, SomeKey_5=SomeValue, SomeKey_6=SomeValue, SomeKey_7=SomeValue, 
SomeKey_8=SomeValue, SomeKey_9=SomeValue}

The closest way of doing this in C# is a bit of a hack using the System.Collections.SortedList and the Comparer classes and its specific to what I was trying to do.I replace the regex ~ with a series of 9's.
 public static SortedList SubMap(SortedList list, System.Object lowerLimit, System.Object upperLimit) {
            string upperStr = upperLimit.ToString();
              if (upperStr.EndsWith("~"))
                upperLimit = upperStr.Replace("~", "999999999999999");
            Comparer comparer = Comparer.Default;
            SortedList newList = new SortedList();
            if (list != null){
                if ((list.Count > 0) &&(comparer.Compare(lowerLimit,upperLimit) <1)){
                    int index = 0;
                    while (comparer.Compare(list.GetKey(index), lowerLimit) < 0)
                    for (; index < list.Count; index++){
                        if (comparer.Compare(list.GetKey(index), upperLimit) > 0)
                          newList.Add(list.GetKey(index), list[list.GetKey(index)]);
            return newList;
Certainly not clean but its all I can think of right now. For all the C# code floating on the web I couldnt find a SubMap implementation that does what the J2SE classes do. Anyone have any better ideas ?

Wednesday Mar 22, 2006

C#, Assembiles and JITing

The last few days I ve been doing more C# than anything else. Never thought it would come to this! So heres my first post on some notes I ve taken.
In .NET an assembly is a reusable, self-describing, versionable deployment unit for types and resources. Because it is self-describing, it allows the .NET runtime to fully understand the application and enforce dependency and versioning rules. Unlike Java—in which both a stand-alone application and a component library are deployed as a .jar file—assemblies come in four types or formats and essentially consist of a manifest that contains the assembly metadata, one or more modules and other resource files such as icons etc.
  • exe. A console executable. The application must contain one entry point defined in the Main method. There is also a tool called the MSIL Disassembler ( Ildasm.exe ) which is a counterpart of the MSIL Assembler (Ilasm.exe) which takes the portable executable (PE) file that contains Microsoft intermediate language (MSIL) code and creates a text file suitable as input to Ilasm.exe. - talk about round trip decompilation?
  • Library. A library (DLL) that can be used by other assemblies.
  • Module. A nonexecutable collection of compiled code for use in other assemblies.
  • winexe. A graphical Windows executable. The assembly must contain one entry point.
Programmatically a .NET assembly is represented by the Assembly class of the System.Reflection namespace and can be programmatically used to query the assembly. Eg Assembly asm = Assembly.LoadFrom("WSTest.exe");

Another concept similar to Java is that of shared asseblys (just like Jars are shared in an app server) . But .NET does not have the concept of class loaders or class paths so the CLR looks in only once place - the Global Assembly Cache (GAC) which is really a directory (usually %Systemroot%\\assembly or %Systemroot%\\assembly) where the CLR can locate such shared assemblies. There is a tool called the GAC tool (gacutil.exe) to install shared assemblies in the GAC. To share an assembly requires it to have globally unique name (aka a strong name) which consists of the name, version information, culture information, and a public key for cryptography. To create a public key, the .NET Framework provides a strong name tool called sn.exe, which creates a key file that can be referenced in the AssemblyInfo.cs. For my example I used sn–k WSTest.snk and created an Assembly.cs that contained something like

using System.Reflection;
using System.Runtime.CompilerServices;

[assembly: AssemblyTitle("WSTest")]
[assembly: AssemblyDescription("WSTest app ")]
[assembly: AssemblyCompany("Sun Microsystems")]
[assembly: AssemblyProduct("WSTest")]
[assembly: AssemblyCopyright("This is the property of Sun Microsystems ")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("")]
[assembly: AssemblyKeyFile("WSTest.snk")]

Now to create the WSTest.dll with with a strong name: csc /target:library /out:WSTest.dll WStest.cs AssemblyInfo.cs and then stored it in the GAC using gacutil /i WSTest.dll
Boom ! We have a shared assembly. So whats the idea - well the plan was to JIT it up and see what happenes with performance.
Theres a bunch of detail on the MSDN website about how runtime objects are created and another link on pros/cons of JIT'ing Essentially when a reference to an object is first encountered, the JITer loads a stub for each method that matches that method's declaration.When the same method is later invoked, the IL for it is compiled and the stub is replaced with the address of the method's compiled code. This happens each time a method is invoked for the first time, and the resulting native code is cached so that it can be reused the next time the assembly is loaded during that session. The JIT compiled code is not actually stored on disk and reused for subsequent executions of the same application. This actually can be done if needed with what Microsoft calls the Native Image Generator - Ngen.exe that a pre-compiles (pre-JIT) IL application code into native machine language code after installation. . A quick peek inside teh C:\\WINNT\\assembly folder shows that not all shared assemblys are in fact JIT compiled to native code. (see x86 and MSIL as proc arch) . The reason is apparently that JIT performs lots of on-the-fly optimizations when compiling MSIL. Many of these optimizations, particularly those involving the use of registers and memory, are driven by the current demands made on the system. Compiling assemblies in one large batch prevents these optimizations from being made and therefore may actually result in slower final code for the server side execution.

Hmm....Did'nt we do this five or more years back with the JDK 1.1.x HotSpot VM ?

Friday Jan 27, 2006

Are you documented - JWSDP 2.0 on Glassfish - Arrgh !

I wanted to run our Galactica code on Glassfish and do some WS-A work on a couple of Niagara boxes. So I installed Glassfish and then tried to update the container with the JWSDP 2.0. So heres the story, I tried the installer in the console mode since I was telnet'ing to the boxes. If you havent used the console, its painful to say the least. Takes you through a whole set of "Hit 1 to continue, 0 to do this " but it worked fine on the first box. On the second one and any subsequent box I tried, it just silently exited right at the end!
You're left wondering - what the heck ! And like any other developer I did the rinse and repeat - and landed up in the same spot on any machine I touched.
It turns out theres a bug in the installer. If you dont export the display (yes even in console mode) it silently exits at the end. The funny part is on the first box I exported the display to try the GUI (which I didnt use because it was slow over the network) so it worked. Took me a half a day and a bunch of emails to figure out that this is a "known" but "undocumented" bug.
Consider it documented here now :)

Friday Dec 09, 2005

HP's Niagara bashing site

HP put this up earlier, its supposed to be a "HP's Niagara Bashing" site as reported by CNET
It almost reads as though HP fears that which is inevitible in the IT industry - change !
After reading it, I felt more inclined to buy a Niagara box, at least thats what a friend of mine commented :)

Thursday Jun 09, 2005

JavaOne & Microsoft

The JavaOne 2005 is special indeed. This will be the first time Microsoft will be there. There is a special one day Interoperability track scheduled for 06/29 and my BoF-9911 is a part of that. More details here

Wednesday Mar 30, 2005

John Hagel & SOA

So according to what I read here, he encourages CIOs "to approach architecture from the outside-in starting with the challenging issues of orchestrating IT resources across enterprise boundaries and then addressing the easier issues of orchestrating IT resources within enterprises boundaries"
Hmm...This is contrary to what most arhictects I know believe. But it makes good sense. Some customers seem to be using web services heavily to integrate with business partners, mostly because of necessity and partially because of perceived cost reductions.

Thursday Mar 03, 2005

CEC Conference

I presented a Technical Session on SOA and Web Services at CEC (Customer Engineering Conference) this weekend. CEC is an intersting Sun conference witth a set of general sessions by people like Hal Stern,Jonathan Schwartz and other executives; followed by Technical Sessions on various topics.
My talk with Inderjeet Singh was on SOA with Java and we discussed one of my pet projects- the Java Blueprints Catalog

Thursday Feb 17, 2005

Sys-Cons XMLEdge 2005

I did a session on Web Services performance at the XMLEdge 2005 Conference in Boston. Looks like they messed up the description a bit on their web page, but the brochure was printed right.

Friday Jan 28, 2005

More on Interoperability and Document Based Web Services

Part 2 of the white paper has been posted on where we examine some of the strategies discussed earlier around Document Based Web Services, and demonstrate interoperability with C#.

Tuesday Jan 18, 2005

CT Java users group meeting

I did a presentation around best practices for developing webservices yesterday at the Connecticut Java users group. The review and summary can be found here

Friday Nov 19, 2004


So I attended and presented at XML-2004 this year.
It was my first time at this conference, and I have to say this- its by far the neatest XML-Web services conference I ve seen. My paper and tutorial can be found on the conference website at

Monday Oct 18, 2004

Java Conference in Boston

Java Live (Boston)  Oct 17-19

Wednesday Oct 06, 2004

Presentation at Application Development Users Group

Application Development & Deployment UG Session(New York), Oct 6-8



« April 2014