Skip navigation

The Set Up
Every so often a bug will creep into Java 2D that causes a particular rendering operation to be so ridiculously slow that it is almost comical. (Only graphics geeks would find humor in something so mundane, but I digress.) Back in the day, like 7 years ago, we would joke in our staff meetings along the lines of:

   Person:   This rendering op is so slow...
   Audience: How slow is it?
   Person:   It's so slow that it would be faster to air mail each
             individual pixel to Timbuktu and back.

A few years ago when the phrase "web services" became all the rage we had to update the punchline:

   Person:   It's so slow that it would be faster to implement this
             op as a web service and host the server in Timbuktu.

Okay, so you're not laughing, but I warned you earlier, I did. Plus, it doesn't help that our collective senses of humor have all been addled recently by Chet and his killer puns (Minneapplet anyone?)...

But seriously forks, all the web service yumminess being integrated into Mustang has peaked my interest, especially from the desktop Java standpoint. I've seen a few JAX-WS 2.0 tutorials recently (see "Useful Resources" section below), but most are written from the server-side perspective, and therefore start out with "download the latest GlassFish build" or "grab the latest JWSDP release" or something similar. Already that's one too many steps for my lazy self. I'm a minimalist at heart, so what I really wanted to know was how quickly one could get up and running with JAX-WS using only the latest Mustang binaries. And what better way to learn than to bring that old "joke" to life, so I now present to a truly contrived example: a pixel blender service (and client).


 

Step 1: Write and compile the endpoint
First we'll create our Blender endpoint. JAX-WS 2.0 makes this a breeze. All we need to do is define our Blender class and annotate it with @WebService, and then implement a single method (the one that does all the work) annotated with@WebMethod:

package blend.endpoint;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;

@WebService(serviceName="BlenderService")
public class Blender {

    @WebMethod
    public int blend(int src, int dst) {
        int srcA = src >>> 24;
        int srcR = (src >> 16) & 0xff;
        int srcG = (src >>  8) & 0xff;
        int srcB = (src      ) & 0xff;
        int dstA = dst >>> 24;
        int dstR = (dst >> 16) & 0xff;
        int dstG = (dst >>  8) & 0xff;
        int dstB = (dst      ) & 0xff;
        int resA = (int)((srcA * 0.5) + (dstA * 0.5));
        int resR = (int)((srcR * 0.5) + (dstR * 0.5));
        int resG = (int)((srcG * 0.5) + (dstG * 0.5));
        int resB = (int)((srcB * 0.5) + (dstB * 0.5));
        return (resA << 24) | (resR << 16) | (resG << 8) | resB;
    }
}

The "serviceName" parameter on @WebService is optional, as are many of the web service related annotation parameters, but here I just wanted to be a bit more explicit about the name of the service being generated. (And again, this isn't meant to be an all-encompassing tutorial; I'm just trying to show the minimal number of steps to get "something" up and running.)

As the final part of this step, we will simply compile this source file:
% javac -d build src/blend/endpoint/Blender.java


 

Step 2: Generate the endpoint "portable artifacts"
Now that we have the source code and class file for our web service endpoint, we can use the wsgen tool (part of the JDK) to generate "portable artifacts", or behind-the-scenes implementation, for our web service:
% wsgen -cp build -d build -wsdl blend.endpoint.Blender

We pass the -wsdl argument to indicate that we wantwsgen to generate the WSDL descriptor file, which we will need later. In addition to the WSDL file, wsgenwill generate a bunch of class files under the build directory. Optionally you can pass the -keep argument indicating that you'd like it to generate the source files as well for those generated classes, but again, we're not concerned with whatwsgen generates. That's the benefit of the annotations here; as long as the tool conforms to the specifications of the various annotation types, we shouldn't need to worry too much about what gets generated under the hood.


 

Step 3: Generate the client "portable artifacts"
In Step 2 we used wsgen to generate a WSDL file from our Java endpoint class. In this step, we will essentially reverse the process and use that WSDL file and the wsimporttool to generate our Java client class:
% wsimport -p blend.client -d build -wsdllocation http://127.0.0.1:8084/blend?WSDL build/BlenderService.wsdl

The -p blend.client argument indicates that we'd like wsimport to generate the class files in theblend.client package to keep them separate from our endpoint artifacts. The -wsdllocation argument is not strictly necessary, but it is important if we want our client to pull the WSDL descriptor directly from the web service endpoint (otherwise our client would try to pull the descriptor at runtime from the location on disk, which is not what we want; this was tricky, it took me a while to figure out why this was needed).


 

Step 4: Write and compile the client application
Now that we have our automagically-generated Blender client classes that know how to communicate with the Blender endpoint, we just need to write an "interesting" client application to consume our even more "interesting" web service. I've written a small Swing client application called BlendClient (source code included in the zip file mentioned later), but it would be easier to illustrate how to use the client code with a simple testcase:

package blend.client;

public class Test {
    public static void main(String[] args) throws Exception {
        BlenderService service = new BlenderService();
        Blender blender = service.getBlenderPort();
        int src = 0xff00ff00;
        int dst = 0xff0000ff;
        int res = blender.blend(src, dst);
        System.out.printf("src=%08x dst=%08x result=%08x\n",
                          src, dst, res);
    }
}

Note that by default this testcase will talk to the web service endpoint running at http://127.0.0.1/blend by virtue of the -wsdllocation parameter specified in Step 3. If we wanted this testcase to communicate with an endpoint published on a different server, we could specify that with some additional parameters to the BlenderService constructor, but for the purposes of this demonstration, the default values will be sufficient.


 

Step 5: Publish the endpoint
Now it's time to publish our endpoint, which is a fancy way of saying that we'll start our "server". Again, I've written a Swing client to start/stop the endpoint, but it would be easier to demonstrate how easy it is to publish an endpoint with a simple code example:

package blend.endpoint;

import javax.xml.ws.Endpoint;

public class Test {
    public static void main(String[] args) throws Exception {
        Endpoint e = Endpoint.create(new Blender());
        e.publish("http://127.0.0.1:8084/blend");
        System.out.println("Endpoint running");
        try {
            Thread.sleep(300000);
        } catch (InterruptedException ex) {
        }
        e.stop();
        System.out.println("Endpoint stopped");
    }
}

This code will publish the web service endpoint on the local machine on port 8084, and will keep it running for 5 minutes. You can run compile and run this testcase easily:
% java -cp build blend.endpoint.Test

Note that the Endpoint API in Mustang is mainly useful for testing purposes. If you were to publish this for real, you'd probably want to deploy it on one of them application server things the server-side folks like to talk about these days.


 

Step 6: Run the client application
This would be a good time to unveil my ludicrous attempt at a Swing application to demonstrate this stuff. You'll need the latest version of Mustang installed to run these WebStarted apps. Note that I've had to sign my jar and give the application all permissions since it needs to talk to the network, even though it is running entirely locally on your machine. (A real web service client won't necessarily have such a restriction; it should be possible to deploy a sandboxed WebStart application as long as it talks back to the server from which it was deployed, at least in theory.)

First fire up the endpoint (Blender Endpoint) using this link (thanks to Romain for hosting the binaries):

http://weblogs.java.net/blog/campbell/archive/webstart/webstart.png


 

Then once the endpoint is running, try running the client app (Blender Client):

http://weblogs.java.net/blog/campbell/archive/webstart/webstart.png


 

Click the button and behold, it should look something like this:

http://weblogs.java.net/blog/campbell/archive/images/blender.png


 

If all goes well, you should see the blended image filling in very, very slowly in the middle of the window. Basically, the app is consulting the Blender web service running on your machine, one pixel and one scanline at a time. Scary, and useless! (By the way, the source code for this application demonstrates a couple other Mustang features, namely SwingWorker and the Desktop integration API, in case you're curious.)


 

Conclusion
Whew! This turned out to be a much longer exercise than I was expecting, but I hope this information will be useful to those getting started with JAX-WS in Mustang, especially from the client side. The minimalist within me winces when I realize how much prose and code it took to explain all this stuff. I'm sure this process is much easier than it would have been in earlier JAX-RPC releases, so I can appreciate the simplicity of the new annotations. However, there seem to be plenty of ways to shoot yourself in the foot when using these annotations, and the current documentation for many of these classes is not entirely clear in my opinion. (Fortunately, I think that once there is more tools support for developing web services, developers will have less opportunity to screw up, and I would hope that the devlopment/deployment experience in those tools will improve things as well.)

At some point I'd like to go into more detail about my issues with JAX-WS, but I've got a plane to catch! I'll be away for a couple weeks, so hopefully you'll understand if I don't respond to any comments right away.


 

Useful Resources





In my ears: Destroyer, "Destroyer's Rubies"
In my eyes: Lots of blurriness at the moment

campbell-3072198

Five Easy Pieces Blog

Posted by campbell-3072198 Apr 23, 2006

I'm looking forward to a 2-week vacation in Greece that begins this week, so my mind is prone to wandering these days. Therefore, I wanted to share with you a few short tidbits about some recent/nifty Mustang (Java SE 6) putbacks before they are indefinitely purged from my brain. These fixes and features are small compared to most others in Mustang, but sometimes it's the little things that help round out a release, even for one that already contains so much desktop goodness. And away we go, in no particular order...


 

AlphaComposite.derive(float alpha)
(RFE 5095630)

It's super easy to code up a fade effect in Java 2D using the AlphaComposite class, but eventually your fingers will tire of writing the following verbose code:

    g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                                                alpha));

Carpal Tunnel? Suffer no more! Mustang will save you a few keystrokes:

    g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));

Clear, concise, nice! This new method saves Romain so much time that he can invent twice as many ridiculous office weapons as before. I recently had to work on a project that needed to run on JDK 5, and if there was one thing I wish I had from Mustang, it was AlphaComposite.derive()!


 

GTK L&F Default Font
(Bug 6378474)

In Tiger and earlier builds of Mustang, Swing's GTK L&F would simply use the default "Dialog" font. In most cases, the chosen font would not be the same as the one used by native GTK applications, and as a result Java applications would look woefully out of place. However, as of Mustang b77, the GTK L&F now consults the fontconfig library, so Swing applications will pick up the same desktop font used by native GTK applications. Clearly this fix is long overdue; it required quite a bit of work, so I'm grateful that Phil fixed this for Mustang (and now I don't have to pester him anymore)! Native fidelity of the GTK L&F has improved considerably in Mustang, and this fix played no small part, but in my opinion we still have a ways to go, so keep those contributions coming...


 

Double Precision for GeneralPath
(RFE 4172661)

This issue has come up at every Java 2D BOF at JavaOne for as long as I can remember, and I'm glad to say that this year we'll finally be able to say that it's available in Mustang. The GeneralPath class has always looked a bit strange without a "double" counterpart, and beyond the obvious symmetry issue, GeneralPath has not been sufficient from a technical standpoint for many advanced users, especially those in the GIS and mapping industries (see the bug report for one such justification).

This is another long overdue enhancement, but it required a surprising amount of investigation to come up with a solution that fits in well with the existing java.awt.geom.* class hierarchy, performs well, and is easy to use. The solution that Jim devised, with help from our community friend "leouser", required lots of thought, but it was worth the wait. There is now a Path2D class (with Float and Double variants) in the java.awt.geom package, which will look familiar to longtime Java 2D users. The GeneralPath class is now a trivial subclass of the Path2D.Float class, so your code will continue to work as always with single point precision, but now you have the option to migrate to the new Path2D.Float/Double classes to make the choice of storage a bit more explicit in your code.


 

OpenGL Acceleration of LCD Text
(Bug 6274813)

If you've read Chet's blog or articleon the subject, you'll know that it takes quite a bit more computation to get LCD-optimized text onto the screen (when compared to grayscale or monochrome text). As of Mustang b77, the text rendering codepath in the OpenGL-based Java 2D pipeline has been revamped to allow for acceleration of LCD text, in addition to the existing (and very fast) grayscale/monochrome codepath.

This is especially noteworthy because it is the first time we have leveraged programmable fragment shader technology in Java 2D. We spent a number of weeks experimenting with a number of approaches in order to achieve maximum performance, and that work has mostly paid off. The performance benefits aren't so noticeable on first and second generation shader-level hardware, but advancements are happening so fast in the graphics hardware space that I think this work will really pay off on today's hardware and beyond (think Nvidia GeForce 7800, ATI Radeon x1800, etc).

Unfortunately when this work was putback last month, there were a couple unresolved driver bugs that prevented us from enabling this support by default (see the above bug report for more details on performance as well as the magic incantation to enable this new codepath). Since then, I'm happy to say that both Nvidia and ATI have addressed those issues, but the fixes are not yet in publically available drivers. Once we're satisfied with those driver fixes, we should be able to turn this codepath on by default, when the OGL pipeline is enabled of course.

While we're on the subject of driver fixes, I'd like to quickly say thanks to Andy Ritger and team at Nvidia, and Matthew Tippett and Balaji Calidas at ATI, for all their help the past couple years in resolving the remaining driver kinks affecting Java 2D and our OGL pipeline. It's been a lot of work, but the collaboration has paid off and I'm hoping that by the time Mustang ships the last couple outstanding driver issues will be resolved.


 

Multi-stop Linear/RadialGradientPaints
(RFE 6296064)

If you're familiar with the Batik SVG library, you may have used the multi-stop gradient paint classes that are included with it. Those classes were originally written by an intern on the Java 2D team, Nico Talian, way back in the year 2000 (when I was just a lowly intern myself!). The code was donated to the Batik project at that time and the classes have served their purpose well, but in recent years there have been quite a few requests to include these classes with the core JDK. Well, mostly it's been Josh, Richard, and Romain who have been doing the requesting on behalf of SwingLabs (check out their recent work on "painters"), and eventually they wore me down enough so that I'd take on this project. So, we cleaned up the original API and got it into Mustang just in time for b81.

The JavaDocs for these new classes include a few basic (yet pretty) images to illustrate the various options:



 

You can also download the latest Mustang snapshot and try the updated "GradAnim" pane in Java2Demo, which demonstrates these new multi-stop gradient classes. (Oh, and before anyone yells at me for not mentioning this, the standard disclaimer applies that these new classes are not officially "in" until the Mustang JSR community -- and the proverbial fat lady -- say so.)

The biggest benefit of having these classes in the core JDK, other than the convenience factor, is that we are now able to provide hardware acceleration for these operations, which has been impossible up until this point. As you may know, the OpenGL pipeline already acceleratesthe basic GradientPaint and TexturePaint operations in hardware for ridiculously good performance (GradientPaint for example is as much as 25x faster via OpenGL than in software!)... When I get back from vacation, I hope to provide similar acceleration for LinearGradientPaint and RadialGradientPaint when the OpenGL pipeline is enabled. More fun with fragment shaders awaits!


 

More to come...
Man, I really thought this was going to be a short entry, but I guess even the smallest of features deserve their fifteen minutes in the spotlight. Stay tuned for an upcoming blog entry containing a minimalist's tutorial of the new JAX-WS support in Mustang, from a client-side guy's perspective of course. I was hoping to get that one out this week, but then again, it would be so much easier to just glide into vacation mode...


 

In my ears: Neu!, "Neu! 2"
In my eyes: Joseph Campbell, "The Hero with a Thousand Faces"