Wednesday, July 15, 2009

FlexMonkey 1.0 Released

My entries here are usually about Java, but today I want to call attention to the 1.0 release of FlexMonkey, an Adobe AIR app for testing Flex and Adobe AIR applications (I work for Gorilla Logic, the creator of FlexMonkey). If you have a need to run automated tests of Flex and AIR apps, please give it a try. If you are familiar with "old" versions of FlexMonkey (as in, 2008), take another look at it; it has evolved quite a lot during its short lifetime!

To learn about FlexMonkey and to download, go to http://flexmonkey.gorillalogic.com/. Don't let the "Beta 1" fool you; I've been using FlexMonkey since its birth and it is significantly more polished than a certain operating system I use every day. There is also a site describing Gorilla Logic's Flex service offerings at http://www.gorillalogic.com/what.development.services.flex.html.

Finally, I should mention that you are not alone should you become a FlexMonkey user. I've been on a lot of support mailing lists in my time, and I am very impressed by the dedication of the user community (including a number of Gorillas!) who respond to questions. So by all means give it a try with the confidence that you'll have all the help you need for both "newbie" and more advanced questions!

Saturday, July 4, 2009

Instrumenting running Java processes with the com.sun.tools.attach API

In recent posts, I've investigated using the java.lang.instrument package to instrument Java classes for a simple profiler. The package summary documentation describes a standard method for instrumenting a target Java application at launch time (using the static premain() method of the profiling agent). It also describes the outline of a procedure to attach to, and instrument, an already-running application (using a static agentmain() method). As the package documentation says, the details of how to initiate this process attachment are implementation specific. In this entry I'll describe how this process works for Sun Hotspot JVMs.

This subject has been discussed in a few other blogs (search on the package name); I'm sharing my experience because some of the pitfalls haven't been covered too well in other postings, and the more hits you can get on a particular stack trace, the better.

If you've used JDK tools like Java VisualVM or JConsole, you've seen the attach API at work. If you're going to attach to a running process, you need to get a handle for it (a VirtualMachineDescriptor, specifically), which implies a method for listing processes (namely, VirtualMachine.list()). These are the process descriptors you see in the above tools' connect dialogs at launch time. The best place to start is to look at the API documentation for com.sun.tools.attach. This information can be found in the tools documentation of your JDK documentation download, or online. From there, take a look at the documentation for the com.sun.tools.attach.VirtualMachine class -- it provides an outline for getting started. I'll start with this, pointing out some of the issues you can run into along the way.

The basic process is outlined below:
  1. Create a public static void agentmain() method in your profiling agent (if you have already created a launch-time profiling agent, this can contain the same logic as your premain() method).
  2. Add an Agent-Class: attribute to your profiler's manifest file, specifying the fully-qualified name of your profiler class
  3. In your launch application, perform the following steps:
    • Obtain a descriptor for the target JVM.
    • Obtain a VirtualMachine instance with the static VirtualMachine.attach() method.
    • Load your profiling agent into the target JVM with the VirtualMachine.loadAgent() method.
    • Detach from the target JVM
  4. At this point, your profiler's agentmain() method will be called and you are in business.
Note that on Windows, a String version of the target JVM's Windows process ID can be passed as the id for the VirtualMachine.attach() method.

A number of issues can arise during this process. First, note that you need the full JDK, not just the JRE, to do this. The reason is that you need the JDK's tools.jar file.

One of the first issues you might see is the following:

java.lang.UnsatisfiedLinkError: no attach in java.library.path
AttachNotSupportedException: no providers installed
com.sun.tools.attach.AttachNotSupportedException: no providers installed

My diagnosis of this problem is that I was executing java on the command line, which as a result of my JDK and JRE installation choices was pointing to the java.exe in the %JAVA_HOME%/jre/bin directory. While this is technically the same executable, you will find that when using the instrumentation packages, the java executable expects to find tools.jar using a relative path, and that path is relative to the java.exe in the %JAVA_HOME%/bin directory. So ensure at launch time that you are running the java executable in the JDK bin, not the JRE bin, directory.

Another more interesting problem you can run into when adding your agent jar to the target JVM is that the target JVM doesn't necessarily know about the dependencies of your agent. For example, all agents will have a dependency on tools.jar. The one I have been building has an additional dependency on the Javassist javassist.jar file. If your target application does not have these files on its classpath, it will fail to start your agent. For the record, in the console window (if you have one) for your target JVM (not the console from which you are trying to attach), you'll see something like

Exception in thread "Attach Listener" java.lang.NoClassDefFoundError: com/sun/tools/attach/AgentInitializationException

But you can't very well require that all potential target applications be launched with your dependencies on their classpath. So what to do?

The answer lies in the Boot-Class-Path: attribute in the profiling agent manifest file (see the package summary documentation for java.lang.instrument). Entries in this attribute are space-separated jar files. So to get past this issue, I added the paths to tools.jar and javassist.jar to this attribute in my profiler's manifest file.

At this point, you should be able to launch your agent. When you launch your profiler agent at target JVM launch time, your class transformer will be called as each class is loaded. When you launch your profiler by attaching to an already-running JVM, you will need to transform classes which have already been loaded. In my profiler, I do this by calling the getAllLoadedClasses() method of the java.lang.instrument.Instrumentation instance, then calling retransformClasses() on them. As you retrieve this set of classes, you might notice that you can check to see if the class is modifiable (using another method in the Instrumentation instance).

Note that according to the java.lang.instrument package summary documentation, you need the manifest attribute Can-Retransform-Classes: to be able to retransform a class. This attribute is not related to instrumenting at launch time versus after launch time (in either case, you could instrument the target application by transforming its classes only one time), but rather is related to the issue of transforming the same files multiple times (for example, to remove your profiling code). I'll discuss retransforming classes a little later.

Another important point to keep in mind is the call to add your transformer to your Instrumentation instance: this should set the canRetransform attribute to true (use the 2-argument signature addTransformer(ClassFileTransformer, boolean), since the default value for the attribute is false). Although the class files had not yet actually been transformed at attach time (when I launch my profiler by attaching to an already-running JVM), I noticed in my own case that if the canRetransform attribute is false, none of my methods get instrumented. I believe the reason for this behavior is simply that I am calling retransformClasses() when I attach to an already-running JVM (I see no other available method to use), and that method will not invoke the transformer's transform() method if the canRetransform attribute is false.

I specifically ignore the classes of my own profiler when I perform this operation -- in my case I get a stack overflow error if I try to method-entry and method-exit instrument the very methods which process these events! You may have similar issues if you are building a similar type of profiler. I also sometimes get odd and unrepeatable (including core-dumping) behavior if I attempt to instrument core Java classes, so I am more inclined to exempt those from profiling (at least of the method-entry/exit kind).

An interesting question to consider is what happens when you terminate your profiling agent. Mine is included in a Swing application. Note that since your agent is launched in the same JVM as the target, you might not want to call System.exit() anywhere -- your profiler users (including yourself) may not want to kill their target application just to close your profiler. But thinking about this brings up another interesting question -- when you close your profiler, what happens to the instrumented classes in your target application? The short answer is that they continue to be instrumented, possibly at significant cost to performance. You don't want to force your profiler users to bounce their application to rid themselves of your bytecodes. So the question is: how to get the target application back to its pre-instrumented state?

Some clues to this question can be found (again!) in the package documentation, specifically the retransformClasses() method of java.lang.instrument.Instrumentation. The description of this method states that this method starts with the intial class bytes of the class, before it was transformed (some differences may exist with the original class bytes, but they are considered cosmetic in the classfile-structure sense). Further reading of this description suggests that we should be able to revert to the original class bytes by (1) removing our current bytecode-manipulating transformer from our Instrumentation instance, and (2) calling retransformClasses() on all classes. Doing this should result in reversion to uninstrumented code, something that we should politely do before closing our profiler. Besides, not doing so results in strange behavior if you launch another profiler against the same running JVM, since you will be re-instrumenting classes you had already instrumented with your previous profiler instance.

Looking at what we need to do to make this work, probably the safest thing to do is to always outfit the Instrumentation class with no ClassFileTransformer and call retransformClasses() first, before then attempting to instrument the target JVM's classes. This behavior will ensure that you are always starting with the original class bytes (at target JVM launch time) before you transform. Then, at profiler-close time, perform the same instrumentation-clearing operation. In other words, the code would look something like:

// initialization code:
MyTransformer transformer = new MyTransformer();
instrumentationInstance.addTransformer(transformer);
...
// to instrument, first revert all added bytecode:
instrumentationInstance.removeTransformer(transformer);
// call retransformClasses() on all modifiable classes...
instrumentationInstance.addTransformer(transformer);
// call retransformClasses() on all modifiable classes...
...
// before exiting application:
instrumentationInstance.removeTransformer(transformer);
// call retransformClasses() on all modifiable classes...

Next, I will verify that this approach works by implementing the above code and running a debug version of my profiler to see if my instrumentation code has been removed.

My first concession to efficiency was to skip the initial revert-to-uninstrumented step, and only revert when I know that the classes have actually been instrumented. The reason is that this is a fairly expensive operation and it isn't necessary to perform when the target application is being instrumented for the first time.

The second issue I had was specific to Javassist. Once certain operations have been performed on a Javassist class model, that class is "frozen" and cannot be retransformed without first "defrosting" it (see Getting Started with Javassist for details). I noticed that, in spite of the fact that I was creating a new Javassist class instance from classfile bytecodes, I get a "class is frozen" RuntimeException when I attempt to transform classes an additional time, so I got around the issue with code like the following:

CtClass cc = cp.get(javassistClassName);
// for repeated instrument/uninstrument cycles,
// make sure the class is unfrozen before we
// attempt to do anything to it:
if (cc.isFrozen())
{
cc.defrost();
}

Looking at the above code reminds me of a minor point you should know -- the fully-qualified class names that the Java runtime passes to your class file transformer's transform() method are slash delimited, while Javassist expects dot (".") delimiters. So to match names, you need to something like the following:

String javassistClassName = className.replace('/', '.');

where className is the class name as passed in to transform().

The above procedure should allow you to stop and start profiling (by un-instrumenting and instrumenting classes) and also to clean up before you close your profiler application, so you don't leave expensive profiling code in your target application at profiler disconnect time. I tested this procedure in my profiler with a pair of stop/start buttons and using Javassist (and the defrost() method shown above) and verified that the target application does indeed revert to pre-instrumented bytecodes.

I keep saying I'm going to do something really interesting with this profiling data (and I will!), but in the process of investigating this API I decided it merited its own entry. Soon, and maybe in my next post, I'll discuss generating events from this data and running them through an event processor.

Monday, June 29, 2009

Java Profiling, continued

I've been building a Java profiler lately, partly to address what I think are shortcomings in the current set of free/shareware profilers, and also to enjoy the flexibility of obtaining only the profiling information I need. If you've spent much time with profilers, you already know they can have quite an impact on the application they're profiling! My goal with my own profiler is to obtain a narrow set of information, having more control over the size and frequency of the data collection.

One feature I really like about hprof (see http://java.sun.com/developer/technicalArticles/Programming/HPROF.html) is the heap=sites option. If you're not familiar with this option, it is described in the above link, but in short, hprof can provide a profile of the allocation of objects per site, where a site is a unique Java stack trace of a fixed depth. This can be very handy information when you are investigating the heap usage of an allocation, but it is extremely costly to get with hprof (in one recent case I was investigating, starting an application with the hprof agent slowed the application down by several orders of magnitue).

Today I'll discuss how I get this same information without the overhead of hprof. I'll build on my last blog entry about the java.lang.instrument package and combine bytecode injection with the ThreadMXBean to get this same information.

The basic outline is:
  1. Create a java.lang.instrument profiling agent.
  2. Intercept all constructors as they are loaded, instrumenting them on constructor entry and constructor exit. Information passed back to the profiler application will include the thread ID and the size of the object created.
  3. In the profiling agent, keep track of the total memory allocated for each object type, as well as a list of stack trace signatures at object instantiation, with frequency of each. The stack trace is obtained via the ThreadMXBean of the JVM.
  4. Rank objects by total amount of memory allocated, the set of unique stack trace signatures for each constructor, and the number of times the object was instantiated via each stack trace.
This information will approximate the heap=sites output of hprof, and can be obtained more economically for a number of reasons:
  • We can restrict the classes (constructors) instrumented. Note that if we do not instrument every class, we may not see every instance of a particular object's instantiation, but in practice what you usually care about are objects instantiated by your application. Learning in hprof that 20 MB of java.lang.Objects were created as a side effects of a java.* method call usually doesn't help diagnose performance problems.
  • The custom profiler can get this information in a very small fraction of the same time that hprof would require to get it, as it is a much more targeted application.
  • With the custom profiler, we have a lot more control over the interval during which the data is collected. For example, we can clear our profiler's data, initiate an operation in the profiled application, and see just the allocation history for the time period of interest. I actually do not know how to obtain this information with hprof; the heap=sites option dumps its summary at application termination (as a snapshot of the heap at that point in time), which may not be what you want.
As I mentioned in my last entry, I use Javassist (see http://sourceforge.net/project/showfiles.php?group_id=22866&package_id=80766). Javassist will create a Java class representation from the bytecode array, and from there it is very easy to obtain the constructors, using CtClass.getConstructors(). The CtConstructor.insertBefore() and insertAfter() methods round out all you will need from Javassist to instrument your code.

Object size is a pesky issue in Java, especially if you have experienced the convenience of the sizeof() operator. I use the java.lang.instrument.Instrumentation instance, supplied by the JVM in the call to the profiler's premain() method, to get the size of the objects (using getObjectSize(Object)). It isn't exactly clear what size this method returns; the javadocs are a little cagey and it's safe to safe this value isn't a deep copy. While it is possible to drill down through object references in a class and attempt to accumulate a deep-copy value, this operation itself can be very time-consuming, incomplete (due to inaccessible attributes) and slightly misleading (static references, anyone?). Not to mention you can easily get a stack overflow error in the process. It may not be perfect, but I have chosen to simply use the value returned by Instrumentation.getObjectSize(Object), and to keep in mind that what I'm seeing is useful in a rough, rather than a precise, sense.

Since the profiler runs in the same JVM as the profiled application, it is fairly simple to get a reference to the JVM ThreadMXBean, using java.lang.management.ManagementFactory.getThreadMXBean(). Given a thread ID (which you can arrange for your instrumented constructor to pass back to your profiler), you can get the stack trace for the thread with ThreadMXBean.getThreadInfo(long,int). Note that there are several signatures for this method call; remember to pick the one where you specify the maximum depth of the stack trace, as the default value is zero!

The value of collecting information like this in a profiler should be self-evident; however, there are greater opportunities available for this data. For example, a variety of profiling information can be used to generate events which can be used to monitor the health of the profiled system. These don't all need to be just events generated by the profiler; they could include SCM and system-maintenance events, with the result being that maybe you could see that your ASN.1 parser suddenly takes three times as long to parse a switch file since the last change was checked in and rolled out to production. In a later posting, I'll look at how such a system could be implemented, looking at the open-source tools available to analyze this kind of data and make associations between events that can provide much faster resolution of operational and performance issues.


Friday, May 15, 2009

Java Profiling with the java.lang.instrument Package

Lately I've been looking at profilers and wondering how easy it would be to write one, mostly to be able to provide a little more sophisticated guidance to performance investigations. For example, while it is useful to pop a profiler and see what methods are consuming the most time in an application, wouldn't it be even more interesting if your profiler could generate an event any time your application spent more than a specified amount of time in a method, like 25% more than the average time (or fell more than 2 sigma outside the mean)?

In general, getting events when something interesting happens is more helpful than watching a display and waiting for anomalies (how many monkeys does that employ?!). That's why I'm also a little disappointed in the JVM's built-in JMX beans, only two of which (MemoryMXBean and MBeanServerDelegateMBean) implement the NotificationEmitter interface. I would much prefer registering for notifications from the garbage collector JMX bean than triggering verbose gc output (with command-line options) and then following and parsing log output!

In this posting I'm going to show you how quickly you can instrument a Java application, and hopefully it will look easy enough that you will give it a try. First, I'd recommend reading the package documentation for java.lang.instrument. It isn't a large package and the time spent is well worth it.

As usual, let's get the setup out of the way. For pre-requisites (such as Java SDK, Maven, etc.), please see one of my earlier posts. Assuming you have the Java SDK and Maven 2 (along with the usual Maven environment variables created), you're ready to go. In a directory containing your development projects, enter the following:

mvn archetype:generate -DgroupId=com.example.profiler -DartifactId=JavaProfiler

Accept all the defaults and wait for your project directory to be generated.

Change into the new project directory, open the pom.xml file and add the following build plugins:

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</plugins>
</build>

The first plugin specifies Java SE6 (rather than the default 1.3!) and the second one says we're going to be supplying our own manifest file. More on that below.

Our basic profiler is responsible for doing a couple of things:
  1. It must supply a premain method, per the requirements discussed in the package documentation;
  2. It must implement the ClassFileTransformer interface (or delegate to a class which does; we're keeping it simple here).
Rename the App.java class to SimpleProfiler.java. For our first cut, we will not bother parsing agent arguments (again, see the package documentation), and our interface-specified transform class will simply print the name of the class and return null. In other words, as the runtime passes us the bytecodes of each class, rather than manipulate them and return an instrumented version, we'll just note the class name and size and let it go. To save you some time, here is what a simple implementation would look like:

package com.example.profiler;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

/**
* Simple java.lang.instrument profiler
*
*/
public class SimpleProfiler implements ClassFileTransformer
{
protected String agentArgString = "";
protected Instrumentation instrumentation;
public static void premain(String agentArgs, Instrumentation inst)
{
SimpleProfiler profiler = new SimpleProfiler(agentArgs, inst);
}

public SimpleProfiler(String agentArgs, Instrumentation inst)
{
agentArgString = agentArgs;
instrumentation = inst;
instrumentation.addTransformer(this);
}
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException
{
System.out.println("SimpleProfiler.transform(): class: " + className + " (" + classfileBuffer.length + " bytes)");
return null;
}
}

This class
  1. provides the premain method that the runtime needs to launch this profiling agent when the target application launches
  2. adds itself as a class file transformer using the Instrumentation object passed to it by the runtime
  3. implements a very simple transform method (returning null means the transformer does not modify the class file)
Now, the runtime will need to know how to launch the profiler, and that is why you need a manifest file. Create the directory structure resources/META-INF under the src/main directory, and add the following single-line MANIFEST.MF file:

Premain-Class: com.example.profiler.SimpleProfiler

Now you should be able to build with

mvn clean install

To see our profiler in action, modify the command line of any Java application you have with the following:

-javaagent:<path to your profiler project directory>/target/JavaProfiler-1.0-SNAPSHOT.jar

This option will cause the premain method of the profiler to be called before the target application's classfiles are loaded, and will give the profiler an opportunity to modify their bytecodes as they are loaded.

I ran a simple Java-based JDBC query tool with this agent and my output from the transform method looked like the following:

SimpleProfiler.transform(): class: Query (21580 bytes)
SimpleProfiler.transform(): class: java/sql/SQLException (2836 bytes)
SimpleProfiler.transform(): class: java/awt/Component$2 (854 bytes)
SimpleProfiler.transform(): class: java/awt/Component$4 (777 bytes)
SimpleProfiler.transform(): class: java/sql/ResultSet (8938 bytes)
SimpleProfiler.transform(): class: java/sql/Wrapper (337 bytes)
SimpleProfiler.transform(): class: java/sql/ResultSetMetaData (1045 bytes)
SimpleProfiler.transform(): class: java/awt/AWTEvent$1 (539 bytes)
SimpleProfiler.transform(): class: sun/awt/AWTAccessor$AWTEventAccessor (270 bytes)
SimpleProfiler.transform(): class: sun/reflect/misc/Trampoline (482 bytes)
SimpleProfiler.transform(): class: sun/reflect/GeneratedConstructorAccessor1 (1277 bytes)
SimpleProfiler.transform(): class: sun/reflect/GeneratedMethodAccessor1 (918 bytes)
SimpleProfiler.transform(): class: javax/swing/text/WrappedPlainView (7040 bytes)
SimpleProfiler.transform(): class: javax/swing/text/BoxView (8593 bytes)
SimpleProfiler.transform(): class: javax/swing/text/CompositeView (6079 bytes)
SimpleProfiler.transform(): class: javax/swing/text/WrappedPlainView$WrappedLine (5054 bytes)'

and so on. You are probably thinking it would be nice to restrict the set of classes that are inspected (and possibly instrumented). This is where the agent argument string (passed to premain) comes in handy. This is a single java.lang.String, rather than the usual array as in the main method, but it's a simple matter to parse one. For example, you could pass in a comma-delimited list of regular expressions to determine what classes get inspected and which do not. If we don't do a little filtering, our profiler's performance will suffer and any output we create on the instrumentation process will be overwhelming, so I will provide sample argument-parsing code in the example without spending time discussing it.

I should say at this point that I've seen a number of blog postings on this subject, all of which stop here and say "Next, we'll use an open-source package to insert bytecodes etc.!". I have yet to see a follow-on where this actually occurs. I was thinking of putting this in a follow-on posting, but I got here (Starbucks) by bus, and on the off chance I get run over catching one home, let's go ahead and do something meaningful with the bytecodes before we end this post.

I looked at ASM, BCEL and Javassist, and decided on Javassist (Why? Not too much -- and not too little -- 'getting started' documentation.) Having tried it, it was so easy to use I haven't tried any of the others yet.

As you would expect, Javassist builds a representation of the class file by inspecting the bytecode array. It also includes some very handy methods for inserting blocks of code (which you specify in String form) at the beginning and ending of methods (among many other possibilities, of course). "Insert before" and "insert after" are exactly what I want for this profiler. I want to insert code to print out an "entry" or "exit" message in every method.

A simple way to do this with Javassist is to modify the transform method as below (preceded by some import statements). Note the addition of the regular-expression-processing code:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.ByteArrayClassPath;
import javassist.CtMethod;

There is a new attribute for the profiler:

protected Pattern[] classRegexPatterns = null;

which gets initialized at the end of the constructor:

// agentArgString is list of regexes of classes to instrument:
if (agentArgString != null && agentArgString.length() > 0)
{
String[] classRegexes = agentArgString.split(",");
classRegexPatterns = new Pattern[classRegexes.length];
for (int i=0; i<classRegexes.length; i++)
{
classRegexPatterns[i] = Pattern.compile(classRegexes[i]);
}
}

Now, for the new transform method:


public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException
{
for (int i=0; i<classRegexPatterns.length; i++)
{
Matcher matcher = classRegexPatterns[i].matcher(className);
if (matcher.matches())
{
try
{
// first modify the class name for javassist. convert slashes to dots:
String javassistClassName = className.replace('/', '.');
// turn bytecodes into a Javassist CtClass
ClassPool cp = ClassPool.getDefault();
cp.insertClassPath(new ByteArrayClassPath(javassistClassName, classfileBuffer));
CtClass cc = cp.get(javassistClassName);
// add output at the beginning and end of each method
CtMethod[] methods = cc.getMethods();
for (int k=0; k<methods.length; k++)
{
// do not instrument inherited methods:
if (methods[k].getLongName().startsWith(javassistClassName))
{
methods[k].insertBefore("System.out.println(\"Entering " + methods[k].getLongName() + "\");");
methods[k].insertAfter("System.out.println(\"Exiting " + methods[k].getLongName() + "\");");
}
}
// return the new bytecode array:
byte[] newClassfileBuffer = cc.toBytecode();
return newClassfileBuffer;
}
catch (Exception exc)
{
System.err.println(exc.getClass().getName() + ": " + exc.getMessage());
if (!(exc instanceof CannotCompileException))
{
// don't care about the stack trace if there was no method body, etc.:
exc.printStackTrace();
}
}
}
}
return null;
}

Note that the Java runtime returns class names slash-delimited, while Javassist expects them to be dot-delimited. Other than that detail, Javassist has no trouble creating and manipulating class file objects from our byte arrays. Also note that I have chosen not to instrument all methods. The Javassist full method name starts with the Javassist class name. To avoid instrumenting methods in parent classes (which normally results in Javassist's throwing an exception because it does not have access to the method body), I have chosen to restrict instrumentation to the methods defined for the class in question. You can remove the if statement and see what happens...

Note the insertBefore() and insertAfter() methods. These are also available for your target application's constructors (retrieved by CtClass.getConstructors()), but I left constructor instrumentation out of this profiler to keep it as short as possible.

To build the profiler, add the following dependency to your pom.xml file:

<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.8.0.GA</version>
</dependency>

and rebuild with

mvn install

Your target application launch will now need to include the Javassist jar file in its classpath. If you have Javassist installed, just point to the jar file there. If not, you can find it in your local Maven repository, as Maven downloaded it to perform the updated build.

Now, relaunch your target application. Note from the package documentation that you pass in your agent argument string at the end of the -javaagent option, following an equals sign. For example, I used:

-javaagent:<path to your profiler project directory>/target/JavaProfiler-1.0-SNAPSHOT.jar=.*Query.*

Note as it runs that you are now seeing console output when methods are entered and exited. In my query-tool example, I see the following:

Entering Query.main(java.lang.String[])
Entering Query.layOutTool()
Exiting Query.layOutTool()
Entering Query.initializeTool()
Entering Query$DatabaseConnection.toString()
Exiting Query$DatabaseConnection.toString()
Entering Query.loadQueries()
Exiting Query.loadQueries()
Exiting Query.initializeTool()
Entering Query.addListeners()
Exiting Query.addListeners()
Entering Query.setInitialDividerLocations()
Exiting Query.setInitialDividerLocations()
Exiting Query.main(java.lang.String[])
Entering Query$DatabaseConnection.toString()
Exiting Query$DatabaseConnection.toString()
Entering Query$DatabaseConnection.toString()
Exiting Query$DatabaseConnection.toString()
Entering Query$DatabaseConnection.toString()
Exiting Query$DatabaseConnection.toString()

and so on.

Following the log output from this profiler isn't that exciting, either! But note what is now available to you. First of all, the profiler is loaded by the same classloader as your target application. This means that if you have methods in your profiler to process method-entry and method-exit events, you can insert bytecodes into the target application to call your profiler (I used a singleton pattern, with a static getInstance() method, etc., in my in-house profiler application). You can insert code to send along the system time stamp, and your profiler can keep a running total of the execution time, number of invocations, percentage of total time, and so on, of the target application. If you do something like this, be sure to include the full method signature in your output, as well as the calling thread's ID, to correctly match your entry and exit events.

Once you are collecting sufficient information, you can start monitoring it for anomalies and generate events (e.g. JMS messages) for incidents you find interesting, and customize your profiler for specific problems and customer situations.

Tuesday, March 24, 2009

Tapping into the GlassFishESB Worklist Manager

I recently investigated the Worklist Manager in GlassFish ESB and want to share some of the details here.  GlassFish is based on the Java Business Integration (JBI) specification and includes service engines for Java EE, BPEL and so on.  The Worklist Manager is one of these service engines.  It is not based on BPEL4People as far as I know but basically meets the same need -- providing a way to integrate human/workflow tasks into a business-process environment.

Note that there is a Wiki for this service engine at http://wiki.open-esb.java.net/Wiki.jsp?page=WLMSE.  My goal when investigating the WLMSE was to demonstrate accessing it from a web service client external to the GlassFish environment.  The documentation in this area is rather spotty, and hence this post.  I started at the above Wiki and took things from there.

An important consideration here is that there are a number of versions of GlassFishESB, OpenESB, NetBeans, and so on, and the combination makes a difference.  If you peruse enough online tutorials, you'll finally begin wondering what version, exactly, you need to download to see the same functionality as you are seeing in the tutorial.  For example, the latest release, recommended on the  WorkList Manager Service Engine (WLMSE) Wiki, is GlassFish ESB V2.1 Milestone 1.  The download for this release includes NetBeans 6.5.  It does not include the WLMSE, however, which the Wiki recommends obtaining as a separate download.  The only problem with the provided download is that it is only compatible with the latest patch of NetBeans 6.1, and lower.  So you will need an earlier version of NetBeans in order to install the WLMSE!

For reference, I used GlassFish V2 UR2 and NetBeans 6.1 Patch 4.  It does not appear feasible at this time to use V2.1M1 with the WLMSE based on the above issue, so I recommend trying something a little older.  My pre-requisites below send you to the older version that I used to develop this post.

Pre-requisites:
  1. Get NetBeans 6.1 at http://www.netbeans.org/downloads/6.1/index.html, selecting the "All" column, which includes SOA support.
  2. Java SE 6 installed: http://java.sun.com/javase/downloads/index.jsp. Be sure to get the JDK, not the JRE, as there are utilities you will need from the JDK.
  3. Maven 2 installed: http://maven.apache.org/download.html
  4. The following environment variables:
    • JAVA_HOME: top-level Java SE 6 directory
    • M2_HOME: top-level Maven 2 directory
    • M2: $M2_HOME/bin or %M2_HOME%\bin
    • MAVEN_OPTS: -Xms256m -Xms512m (optional)
    • PATH: $JAVA_HOME/bin:$M2:$PATH or %JAVA_HOME%\bin;%M2%;%PATH%
The Java SE JDK, Maven 2 and the environment-variable settings are for the Java-based web-service client we'll be building after we deploy an instance of the Worklist Manager.

Create a Worklist Application

Start NetBeans.  Once the IDE is up, select the Services tab, expand "Servers", then right-click on "GlassFish V2" and select "Start".  Once the GlassFish server has started, select the Projects tab.

From the "File" menu, select "New Project...", then select category "SOA", then select "Worklist Module".  Enter "TutorialWorklistApp" for the project name and click "Finish".

Our worklist application will start with a single operation.  Just as an example, assume this is an ETL system and we want to create a trouble ticket on failure of a file to load.  The creation of the application will be a lot smoother if we first create the XML Schema for the request and response messages, then create the WSDL definition to define the operation, then create the worklist Task itself.

The XML Schema

Right-click on the "WorklistFiles" directory under "TutorialWorklistApp", select "New" -> "XML Schema...".  If "XML Schema..." is not listed, select "Other...", then "XML", then "XML Schema", then "Next".  Name the schema tutorial_worklist (NetBeans will supply an .xsd extension) and click "Finish".  The schema will open in a graphical schema editor.  Provide elements for the request and response messages and base them on complexTypes containing information that might be needed for this application.  An example schema is below:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://xml.netbeans.org/schema/tutorial_worklist"
            xmlns:tw="http://xml.netbeans.org/schema/tutorial_worklist"
            elementFormDefault="qualified">
  <xsd:element name="loadFailureActionRequest" type="tw:LoadFailureActionRequestType"/>
  <xsd:element name="loadFailureActionResponse" type="tw:LoadFailureActionResponseType"/>
  <xsd:complexType name="LoadFailureActionRequestType">
    <xsd:sequence>
      <xsd:element name="feedName" type="xsd:string"/>
      <xsd:element name="fileName" type="xsd:string"/>
      <xsd:element name="timestamp" type="xsd:date"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="LoadFailureActionResponseType">
    <xsd:sequence>
      <xsd:element name="troubleTicketID" type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

Save the schema and check it with the "Validate XML" button.

The WSDL definition

Right-click on the "WorklistFiles" directory under "TutorialWorklistApp", select "New" -> "WSDL Document...".  If "WSDL Document..." is not listed, select "Other...", then "XML", then "WSDL Document", then "Next".  Name the WSDL document "tutorial_worklist"  (NetBeans will supply a .wsdl extension) and click "Finish".   Select "Concrete WSDL Document", then "SOAP" Binding and "Document Literal" for the Type.  Click "Next".

In the "Abstract Configuration" panel, clean up some of the names a little.  Change the port type name to worklistPortType, the operation name to processLoadFailure, then click the ellipses ("...") for the Input and Output messages to select the input and output messages we defined in the XML Schema above (namely, loadFailureActionRequest and loadFailureActionResponse; you will need to scroll up in the dialog to find the definition, and expand the "Elements" node).  Leave the "Generate partnerlinktype automatically" box checked.  Click "Next".

On the "Concrete Configuration" panel, clean up the names a little, using worklistBinding, worklistService, and worklistPort for Binding Name, Service Name, and Port Name, respectively.  Click "Finish".

The Task definition

Right-click on the "WorklistFiles" directory under "TutorialWorklistApp", select "New" -> "Worklist Task Definition...".  If "Worklist Task Definition..." is not listed, select "Other...", then "SOA", then "Worklist Task Definition", then "Next".  Name the file processLoadFailure (no need for an extension), click the ellipsis by the empty Operation name, expand the WSDL definition down to the processLoadFailure operation, and click "OK".  Click "Finish".  The Task definition will now open in a graphical editor.

Build

Right click on the name of the Worklist Application ("TutorialWorklistApp") and select "Build".  You should get a successful build.  This application is now a JBI module which can be deployed to a composite application, which is our next step.


Create a Composite Application

From the "File" menu, select "New Project...", then project category "SOA", then "Composite Application".  Click "Next".  Name the project TutorialCompositeApp.  Click Finish.  The Service Assembly file TutorialCompositeApp.casa will open in a graphical editor containing three sections:
  1. WSDL Ports
  2. JBI Modules
  3. External Modules
Drag the Worklist Application (TutorialWorklistApp) over to the "JBI Modules" section of the service assembly and drop it.  At this point, your service assembly will look as follows:



Right-click on the project name and select "Build". You will see the service-assembly view reload, with the following results:



Note that the processLoadFailure operation has a configured SOAP/HTTP binding, as we expected (we just configured it in some detail), but notice that we also have a TaskCommonPort, with no binding.  This is the Worklist Manager.  We will add a SOAP/HTTP binding for this port so that we will be able to access the items in the Worklist from a simple demonstration Java client.

Create the Worklist Manager binding

From the "WSDL Bindings" section of the Palette, drag and drop a "soap" binding on the "WSDL Ports" section of the service assembly.  You will see an additional binding similar to the one already configured for the Worklist application.  Select the purple (right-pointing) arrow in the binding and drag over to and connect to the TaskCommonPort port.  Now, right-click on this binding and select "Properties".  Change casaPort1 to worklistManagerPort, then click on the ellipsis ("...") by the soap:address Location.  You should see the following:

http://localhost:${HttpDefaultPort}/casaService1/worklistManagerPort

Simplify this a little by changing it to

http://localhost:${HttpDefaultPort}/worklistManager

Note that the URL for the Worklist application is similarly wordy.  If you want to shorten it, you can in similar manner, but you'll have to first right-click on the binding and select "Clone WSDL Port to edit...".  Confirm on the dialog which follows, then right-click and select "Properties" to edit, and change the soap:address Location from

http://localhost:${HttpDefaultPort}/tutorial_worklistService/tutorial_worklistPort

to

http://localhost:${HttpDefaultPort}/worklistApp

then click "Close" to save.  Your service assembly should now look as follows:



Deploy the Composite Application

Before we deploy the application, you're probably wondering what ${HttpDefaultPort} means.  You might guess that it is the default port of the GlassFish web services, but actually each of the binding components in GlassFish has their own unique default port.  This can be a problem if you are trying to access a binding component from outside of GlassFish and don't know the port number, but these can be found in the Admin console.  For more information, see "Understanding the ${HttpDefaultPort} Token" at http://wiki.open-esb.java.net/Wiki.jsp?page=ClusteringSupportForHTTPBindingComponent.  But I will walk you through the process here.

To determine what the default port is for a binding component (in this case, the HTTP BC):
  1. Open the Admin console (in Netbeans, open Services tab, expand "Servers", right click on Glassfish, select "View Admin Console".  Unless you changed values, the username/password pair is "admin"/"adminadmin".
  2. Under "Common Tasks", expand "JBI", "Components", "sun-http-binding".
  3. Select the "Configuration" tab.
  4. Second entry is "Default HTTP Port Number".  If you did not change this on installation, it will probably be 9080.
If you choose to, you can always select an unused port number and hard-code it into the bindings before you deploy.  If you're like me, all you care about is being able to find out where the service is deployed!

Now, right-click on the composite application name in the "Projects" tab, select "Build", then again right-click on the application name and select "Deploy".  The project should deploy successfully.  

To verify that the two services are correctly deployed, and assuming you used the soap:address Locations I used above, navigate to the following:


where I have substituted the HTTP default port for my deployment.  Note you may have to view the page source to see the WSDL definition, but you should see both.  Note the worklistApp has a single operation available (processLoadFailure), while the worklistManager has a number of operations related to the processing of tasks, as we might expect.  For example, GetTaskList, RevokeTask, CompleteTask, and so on. We will be calling these operations from the simple Java web-service client which we will create next.

Create a simple Java client

All we'll be doing here is generating the minimal Java client necessary to exercise some operations on the Worklist Manager.  The steps involved are almost identical to those under the heading "Create a client for the web service" at my earlier post Java-SE-Based Web Services .  The only difference will be the URL of the web service, of course.  Because the WSDL definition of the Worklist Manager contains a URL to the TaskCommon.wsdl file (the latter is the abstract WSDL, and the former imports it and contains the necessary information to make the WSDL definition concrete), we will copy both files to a wsdl directory in our client build.

Expand the NetBeans project directory for the composite application and retrieve the following files:
     src/JbiServiceUnits/TutorialCompositeApp.wsdl
     src/JbiServiceUnits/TutorialWorklistApp/TaskCommon.wsdl

Copy these files into the wsdl directory (under your Maven project src directory).  Open the TutorialCompositeApp.wsdl file and change the import path for TaskCommon.wsdl to reflect the fact that it is now in the same directory as TutorialCompositeApp.wsdl:

<import namespace="http://jbi.com.sun/wfse/wsdl/TaskCommon" location="TaskCommon.wsdl">

Also, edit the soap:address, replacing ${HttpDefaultPort} with the actual port number (defaults to 9080, or use whatever value you hard-coded, if any).

Then, instead of configuring a WSDL URL in your pom.xml file, configure a WSDL file as follows:

<configuration>
<wsdlFiles>
<wsdlFile>TutorialCompositeApp.wsdl</wsdlFile>
</wsdlFiles>
<packageName>com.example.webservice.service</packageName>
</configuration>

The client of course instantiates a different service and invokes different operations than in my previous post.  Here is an example Java client that exercises a couple of operations on the Worklist Manager:

package com.example.webservice.client;

import com.example.webservice.service.QueryType;
import com.example.webservice.service.CasaService1;
import com.example.webservice.service.TaskCommonPortType;
import com.example.webservice.service.TaskFaultMsg;
import com.example.webservice.service.TaskListType;
import com.example.webservice.service.TaskType;

public class WLMClient
{
public static void main(String[] args) throws TaskFaultMsg
{
CasaService1 service = new CasaService1();
TaskCommonPortType port = service.getWorklistManagerPort();
System.out.println("Getting Task List:");
QueryType qt = new QueryType();
QueryType.Users users = new QueryType.Users();
users.getUser().add("john");
qt.setUsers(users);
QueryType.Groups groups = new QueryType.Groups();
groups.getGroup().add("CustomerServiceRep");
qt.setGroups(groups);
TaskListType tasks = port.getTaskList(qt, "john", 0, 100);
System.out.println(" Total record count: " + tasks.getTotalRecords());
System.out.println(" Returned record count: " + tasks.getReturnedRecords());
for (TaskType nextTask: tasks.getTask())
{
System.out.println("Task ID: " + nextTask.getTaskId() + "; title: " + nextTask.getTitle() + "; " +
"Claimed by: " + nextTask.getClaimedBy() + "; Assigned to: " + nextTask.getAssignedTo());
}
}
}
Build and run this application and you should see output like the following:

C:\Users\wayne.adams\Blogger\GlassfishWLMJavaClient>java -cp target\glassfishWLMJavaClient.jar com.example.webservice.client.WLMClient
Getting Task List:
Total record count: 0
Returned record count: 0

In this case, there are no tasks, but we can create some by exercising the other service, the worklistApp service. More on that in a subsequent post!

Java-SE-based Web Services

This isn't exactly late-breaking news, as Java SE6 has been out for a while, but the Standard Edition now includes support for non-trivial web-service development and deployment, no Java-EE application server necessary. The purpose of this very quick introduction is to produce a working web service and a web service client, in as few lines of Java as possible, without relying on heavy infrastructure (namely, an IDE and/or an application server). The output of this post will re-appear in subsequent posts, for example when I need a quick client to kick off a BPEL (Business Process Execution Language) process, or simply to exercise a web service to verify deployment.

Pre-requisites:
  1. Java SE 6 installed: http://java.sun.com/javase/downloads/index.jsp. Be sure to get the JDK, not the JRE, as there are utilities you will need from the JDK.
  2. Maven 2 installed: http://maven.apache.org/download.html
  3. The following environment variables:
    • JAVA_HOME: top-level Java SE 6 directory
    • M2_HOME: top-level Maven 2 directory
    • M2: $M2_HOME/bin or %M2_HOME%\bin
    • MAVEN_OPTS: -Xms256m -Xms512m (optional)
    • PATH: $JAVA_HOME/bin:$M2:$PATH or %JAVA_HOME%\bin;%M2%;%PATH%
Create a Web Service project and launch the service

As you've guessed, I'm using Maven to build this service. Maven will generate the project directory for you, so there's no need to create it first. Just open a command prompt or shell in a development directory and enter the following:

mvn archetype:generate -DgroupId=com.example.webservice -DartifactId=JavaSEWebService

substituting your desired package name for groupId. The artifactId will be the name of the project directory Maven will create in your current working directory. You will be asked for an archetype, where the default is 15, or maven-archetype-quickstart. Select defaults by hitting "Enter" until Maven generates the project.

Next, create a simple web service. Note: I prefer to start with a WSDL definition first, then generate the server and client-side artifacts from it, but for this quick example we'll go in the opposite direction, taking a small piece of business logic and exposing it as a service.

cd to the the groupId-defined package under the source branch (./{artifactId}/src/main/java/...) of the newly-created project. There will be a class called App.java. We will ignore this class and create our own.
  1. Create a directory called service and cd into it.
  2. Create a Java class called Tutorial.java.
  3. Add a couple of methods; one called getHelloWorld that takes no arguments and returns the String "Hello, World!" and a second one called getPersonalizedHelloWorld that takes a String and returns a personalized greeting.
  4. Add a main method, and in it, instantiate a Tutorial and publish it, using the javax.xml.ws.Endpoint class, on your machine. Annotate the class as a WebService and the methods as WebMethods.
An example class is shown here:

package com.example.webservice.service;

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

@WebService
public class Tutorial
{
public static void main( String[] args )
{
Tutorial wsInstance = new Tutorial();
Endpoint.publish("http://localhost:8282/tutorial", wsInstance);
}
@WebMethod
public String getHelloWorld()
{
return "Hello, World!";
}
@WebMethod
public String getPersonalizedHelloWorld(String yourName)
{
return "Hello, " + yourName + "!";
}
}
Next we'll modify the Maven "pom" (Project Object Model) file, first to use Java SE 6 (Maven defaults to 1.3) and secondly to specify the wsgen Maven plugin. wsgen creates all the artifacts necessary to build and deploy the web service. cd into the project directory and open the pom.xml file. Add the following to your pom:

<build>
<finalName>tutorialWebService</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>wsgen</goal>
</goals>
<configuration>
<sei>com.example.webservice.service.Tutorial
</sei>
<genWsdl>true</genWsdl>
<keep>true</keep>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Save and close the file. From the project directory, enter mvn install. You should get a clean build. Note that the name of the produced .jar file is obtained from the finalName element in the pom.xml file. Start the web service from the project-level directory with:

java -cp target/tutorialWebService.jar com.example.webservice.service.Tutorial

and verify that the service is available by retrieving the WSDL description in a browser. Append ?wsdl to the endpoint URL you defined in your Java class, in this case

http://localhost:8282/tutorial?wsdl

Note that depending on the browser you are using, you may need to view the page source to see the WSDL description.

Create a client for the web service

We'll create a new Maven project for the client. Open another command prompt or shell in your development directory (again, no need to create the project directory, as Maven will do this for you), and enter the following:

mvn archetype:generate -DgroupId=com.example.webservice -DartifactId=JavaSEWebServiceClient

substituting your desired package name for groupId. The artifactId will be the name of the project directory Maven will create in your current working directory. As for the web service example, you will be asked for an archetype, where the default is 15, or maven-archetype-quickstart. Select defaults by hitting "Enter" until Maven generates the project.

cd into the project directory, given by the value you supplied for artifactId (for this example, JavaSEWebServiceClient). Open the pom.xml (Maven Project Object Model) file. Add the following to the POM; wsimport is used to generate client stubs for your web service:

  <build>
<finalName>tutorialWebServiceClient</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>wsimport</goal>
</goals>
<configuration>
<wsdlUrls>
<wsdlUrl>http://localhost:8282/tutorial?wsdl</wsdlUrl>
</wsdlUrls>
<packageName>com.example.webservice.service
</packageName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Save and close the file. Note that you are directing Maven to obtain the WSDL description via the URL we used earlier to verify that the web service was correctly deployed.

Create a simple web service client:
  1. cd to the groupId-defined package under the source branch (./{artifactId}/src/main/java/...) and create a client directory; cd into this directory.
  2. Create a Java class called TutorialWebServiceClient.java.
  3. Create a main method which gets a reference to the tutorial web service and exercises its two operations. For example:

package com.example.webservice.client;

import com.example.webservice.service.Tutorial;
import com.example.webservice.service.TutorialService;

public class TutorialWebServiceClient
{
public static void main(String[] args)
{
TutorialService service = new TutorialService();
Tutorial port = service.getTutorialPort();
System.out.println("Calling getHelloWorld(); returns '" + port.getHelloWorld() + "'");
System.out.println("Calling getPersonalizedHelloWorld(\"Bob\"); returns '" +
port.getPersonalizedHelloWorld("Bob") + "'");
}
}

Note that the wsimport utility will create an interface named TutorialService.java and a class named Tutorial.java. It will do this before the client is compiled, but if you want to see the stub classes before you build the client, just execute mvn install before creating the client.

The web service interface name is obtained by prepending the package name from the POM to the name of the web service portType from the WSDL description, and the name of the service itself is the serviceName from the WSDL description, if you want to reference the classes before they are generated by wsimport.

Also note that the endpoint URL of the web service was embedded into the generated client-side classes by the setup in the Maven POM file, as I mention above.

From the project directory, enter

mvn install

This will generate the client-side stubs you need in your client, followed by the client itself. You can safely ignore the "[INFO] Nothing to do, no WSDL found!" log message; it is misleading and if you get a "BUILD SUCCESSFUL" message, then all is well.

Test the client and the web service with:

java -cp target/tutorialWebServiceClient.jar com.example.webservice.client.TutorialWebServiceClient

You should see output like the following:

Calling getHelloWorld(); returns 'Hello, World!'
Calling getPersonalizedHelloWorld("Bob"); returns 'Hello, Bob!'

You now have a working web-service/client pair, which will come in handy in future entries as we explore Flex web-service clients, BPEL and JBI.

Introductions

Hello, everyone!

Before I start posting, I should tell you a little about where I'm going.

My main software interest is enterprise Java, and more specifically business process modeling, either on top of Java or a slightly higher level of abstraction.  So I'm really interested in Web Services, Business Process Execution Language (BPEL), Java Business Integration (JBI), and (at this time, to a lesser extent) the Service Component Architecture (SCA).

I'm much more practical than theoretical.  I like to get things working first and ruminate later.  I like tutorials!  I like finding good ones, and I like creating them, especially when it seems there's a hole in the tutorial offerings.  Most of what you see posted here will be in the form of tutorials.  Some of them will be really simple, as they will serve as building blocks for subsequent posts that you'll hopefully find more interesting.  For example, sometimes it's handy to have a 25-line Java-SE-based web service, or an even briefer web service client, to test an application in development (in fact, this will be the topic of my next post).

With that, I hope you enjoy what's forthcoming and that you also find it useful!  Ciao!

Wayne