Friday, September 3, 2010

Getting more out of your debugger with remote options

Introduction
I bet day to day most people use the built in direct debugger in your IDE. You press a button and everything is done for you. This won't help you if the start up configuration is non trivial, in the case of weblogic for example where you have to run a script, or the process has to run on another machine. In the remote section there are a number of options that are pretty interesting as far as dealing with certain corner cases. I had cause to use some of them recently to track down a particularly hard to reproduce bug and realized that some are actually applicable to day to day work.

All the screen grabs are from JDeveloper; but the concepts should work equally well with other tools.

Normal remote debugging.

JDeveloper at least doesn't come with a remote run profile configured by default but it is really quick to set one up from the project properties dialog. In the Run/Debug/Profile configuration page create a new entry:



Then edit the properties so that JDeveloper knows you are doing remote debugging:



Then you can select this profile to debug:



The first time the debugger runs you will see this connect dialog, for most local debugging work the settings can stay the same so check the box so you don't get asked each time. You might want to consider increasing the timeout a little bit for convenience.


So you need to run your java process with the, erm, simple command line option as follows. Not the easiest to remember string; but I guess "alias" is your friend.
java -agentlib:jdwp=transport=dt_socket,server=y,address=4000 ...

Right so that is the basics, lets try some of the more interesting options.

Start and connect later

In the default mode for the debugger the debuggee process won't start until until the IDE tells it to, and will quit when the debugger stops. This is really annoying if you are trying to debug a remote instance of weblogic server and want to keep it running between sessions. (For example if you configured another server to use instead of the integrated one, I am looking at you Chris Muir) Or if you forget to start the debugger and everything is just sitting there waiting for you to wake up.

So you can use the "suspend=n" flag, this means that the target task will start; but allow you to connect a debugger later:

java -agentlib:jdwp=transport=dt_socket,server=y,address=4000,suspend=n ...

You run the remote debugger in the normal way; but when you "Stop" the debugger you now have a choice to either let the process continue running or to kill it:


This allows you to leave infrastructure running, most like a server, whilst still be able to connect on demand to debug.

Have debugger always running, get the debuggee to connect back

The previous two configurations both require at least two step processes for each debugging session. You have to start the debugger and debuggee. This can be really annoying particularly if you are trying to debug a hard to reproduce problem that requires the target VM to be stopped and started many times.
Luckily there is a setting that allows the debuggee to connect to the debugger at start up. The debugger on the IDE only needs to be started once to listen for events. This takes a bit more to set up so lets start at the IDE end and create a new Run/Debug/Remote entry:



In the properties for "Remote - listener" change to be a JPDA listener rather than the normal attach mode:



Also set the timeout to zero so it listens for-ever and use a different port so that it won't clash with normal debug sessions. (Minor mistake in screen grab, the combobox is showing the wrong value use the value in the previous grab)



Now you just have to run this profile in the normal way, and instead of the debugger starting you should see this listener active in the Run Manager.



So the next step to to run the task you want to debug notice the server flag has changed and we have specified the new port:

java -agentlib:jdwp=transport=dt_socket,address=localhost:5000,server=n ...

In JDeveloper the debugger will start up and then run in the normal way:



You can start multiple sessions at the same time with difference instances of the program you are trying to debug. It can get kind confusing though. It does mean that starting the debuggee is a one step process which seems to save me some vital thinking seconds that I could be applied to the problem in hand.

At some point you may wish to shut down the listener, for completeness you can do this from the run manager:




This is the mode that I am finding a valuable time saver day to day. It might only save a few seconds on each run; but over the last week it has proven to be a real time saver. Of course this is very dependent on just what and how you are debugging on whether you will find this useful.

More details

For more details on the debugging options that a look a this sun Oracle document. There are other options such as in memory transports and starting a debugger on an exception that are worth looking at.

Thursday, August 19, 2010

Can you see the problem with this code?

Just a little code quiz, can you spot the design problem with this API? Bonus points for come up with a plausible reason as to what the programmer was distracted with whilst working on this.

    /** Load an integer from system properties, validating in a range */
    public static int getProperty(String name, int defValue, int min, int max){
        try {
            int value = Integer.getInteger(name, defValue).intValue();
            return Math.max(min, Math.min(max, value));
        }
        catch(NumberFormatException e) {
            return defValue;
        }
    }
    /** Load an long from system properties, validating in a range */
    public static long getProperty(String name, long min, long max, long defValue){
        try {
            long value = Long.getLong(name, defValue).longValue();
            return Math.max(min, Math.min(max, value));
        }
        catch(NumberFormatException e) {
            return defValue;
        }
    }

Friday, April 30, 2010

Hello World using the Weblogic SCA support in JDeveloper 11.1.1.3.0

This is just a brief introduction to the newly added Weblogic SCA support that we have in the latest release of JDeveloper. Weblogic SCA allows you to take POJO classes and expose them as Web Services or EJB externally and use Spring internally to connect services together. You can find more on WebLogic SCA in the primary documentation.

Before we get going you are going to need to download the "Spring & Oracle WebLogic SCA" extension for JDeveloper as we don't ship it with the product. So as per normal Help->Check for Updates and select the matching extension:

Once this is installed and JDeveloper has restarted we need to perform an extra step to install the Weblogic SCA extension in the copy of WebLogic that comes with JDeveloper. In the context of a project select Run -> Start Server Instance. Then on the WebLogic console, "http://localhost:7101/console", navigate to "Deployment" then select the "Install" action. You can find the shared library you need to install under %INSTALL_HOME%/wlserver_10.3/common/deployable-libraries/weblogic-sca-1.1.war.

You need to just accept the defaults in the wizard and once finished we are almost ready to start writing code. If your are interested in taking this a little bit further you should go to Preferences -> Extensions and enable the weblogic-sca and spring extension that provide extra monitoring capabilities. These require a server restart to fully enable.

Heading back to JDeveloper now we are going to create a new project and then configure that project for as a Weblogic SCA project that is going to be deployed as a war file. (See documentation for more on deployment options).

This will give you an empty web.xml, an empty spring-context.xml and a weblogic.xml that sets up the dependency on the WebLogic SCA library. The final file looks a lot like this, in a more complicate environment you might need to specify the library version.

<?xml version = '1.0' encoding = 'windows-1252'?>
<weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-web-app http://www.bea.com/ns/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd" xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app">
    <library-ref>
        <library-name>weblogic-sca</library-name>
    </library-ref>
</weblogic-web-app>

We are going to create a few java classes to illustrate not just the publishing of a POJO as a service but also the wiring up of internal components using Spring. First lets define the service, you need a class and interface for SCA:


// Interface

package hello;

public interface Hello {
    
    public String sayHello(String name, String langauge);
    
}

// Implementation

package hello;

import translation.Translator;

public class HelloImpl implements Hello {
    
    private Translator _translator;
    
    public void setTranslator(Translator t) {
        _translator = t;
    }
    
    public String sayHello(String name, String langauge) {
        String message = "Hello " + name;
        return _translator.translate(message, langauge);
    }
    
    
}

For the purposes of the demo we are going to have to provide this Translator interface along with an dummy implementation again in separate files. The implementation could be from a JAX-WS proxy; but this is wired up in a different way from what we are showing today.


package translation;

public interface Translator {
    
    public String translate(String text, String langauge) ;
    
}

// Dummy Implementation

package translation;

public class DummyTranslator implements Translator {

    public String translate(String text, String langauge) {
        if ("fr".equals(langauge)) {
            return text.replace("Hello", "Salut! ");
        }
        else if ("de".equals(langauge)) {
            return text.replace("Hello", "Guten Tag");
        }
        else {
           return "DT " + langauge + " " + text;
        }
    }
}

So we are going to need two entries in spring-context.xml, the first is to wire the dummy translator up to HelloImpl:

  <bean id="translator" class="translation.DummyTranslator" />
  <bean id="hello" class="hello.HelloImpl">
     <property name="translator" ref="translator" />
  </bean>

One difference you might notice from the previous spring extensions is that the bean names and class names are now connected up to the Refactoring framework. So you can "Rename", "Find Usages" and "Go to Declaration" on most of the entries in this file.

The next entry in this file exposes the HelloImp as a web service using the binding.ws elements. So the final spring-context.xml becomes:

<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool-2.5.xsd http://xmlns.oracle.com/weblogic/weblogic-sca META-INF/weblogic-sca.xsd">
  <!--Spring Bean definitions go here-->
  
  <bean id="translator" class="translation.DummyTranslator" />
  <bean id="hello" class="hello.HelloImpl">
     <property name="translator" ref="translator" />
  </bean>
  
  <!--Service definitions -->
  
  <sca:service name="helloService" target="hello" type="hello.Hello">
    <binding.ws xmlns="http://xmlns.oracle.com/weblogic/weblogic-sca-binding"
                port="hello" uri="/hello" soapVersion="1.1" name="hello"
                location="HelloLocation"/>
  </sca:service>
  
  
</beans>

So you can right click and "Run" spring-context.xml and everything gets deployed to the local application server. The easiest way to test the service is View -> Application Server Navigator and then select the deploy service from the "Web Services" node.

And then you will see the test page for this service, just a quick overview but enough to get starting with this new area of functionality. Note that unlike full SOA this feature is part of the standard WebLogic license. Also that if you have SOA tooling installed you will see more integration with tools such as the composite editor which "knows" about Spring SCA beans.

Thursday, April 15, 2010

ADF/JSF Invoking a method on a backing bean before the page is shown

In a couple of cases in our application we really needed to run a little section of code before the page was displayed. We tried a bunch of ways of doing this until we got a tip from Ducan Mills that we could consider abusing "Bookmark" feature in adfc-config.xml. This is normally just for accepting query parameters; but it turns out that the method is executed before the page is rendered so it can be useful it you need to do some minor configuration in advance.

Note that this only applies to unbounded task flows, bounded task flows can't be made book markable. Also the parameters are optional, the code will fire even if none are defined as they are in this example. (We are using the code to force some initial selections in this case.)

I am not convinced it is a good idea in many cases; it was really helpful in solve our particular problem. I would be interested to hear if there is a more JSF way of doing this.

Forgotten JDeveloper feature : The bytecode debugger

I am pretty lucky in my day to day work that I have access to nearly all of the source code for the software I work with. (Even the close hold Java security code which is very useful when working on the HTTP Analyzer). Sometimes you run across a snippet of code that for whatever reason you don't have the source code to hand. JDeveloper will generate you a stub; but that is not so useful when you want to know what is going on.

There is a kind-of hidden feature in JDeveloper called the bytecode debugger that you can enable by selecting "Show Bytecode":

This will show you the method in question in terms of bytecodes, and you can use the new extra pink toolbar icons to step at the bytecode level or just use the normal ones to step in at the java method level.

Very useful when you are stuck and for very good legal reasons cannot make use of java dissembly tools.

Wednesday, April 14, 2010

Making OTN documents more readible

There is a lot of good information on OTN; but I like a certain subset of other people find the color scheme hard to read. For example I find the this page by Steve really hard to read because of the large amount of bold text:

To me I can only focus on the bold text and the rest just kind of swims around. This might be because I might be slightly dyslexic, never confirmed, or just my eyes are tired this week. Either way it means I have to print everything on OTN in order to be able to read it.

The author of this page suggested I hack the .css using firebug; but I wanted a more automatic option so after discarding GreaseMonkey and being too much hassle for this work I settled on a addon called Stylish. This simply allows you to override .css settings for particular websites. Once installed, and restarted, you invoke it from the icon that gets installed at the bottom left of Firefox window:

You can then play with the .css to you hearts content using the style editor dialog:

The style I am using removes a lot of the bold text and gives me a nice yellow background color this I find easier to read on. I would be interested to see what other people choose, I know there have been some moans about the new red/black color scheme on dev.java.net.

@namespace url(http://www.w3.org/1999/xhtml);

@-moz-document domain("www.oracle.com") {


.boldbodycopy {font-family: Arial, Helvetica, sans-serif; font-size: 12px; line-height: 14px; font-weight: normal !important; color: #000000 ; text-decoration: none; }

.boldbodycopy2 {font-family: Arial, Helvetica, sans-serif; font-size: 12px; line-height: 14px; font-weight: normal !important; color: #999999 ; text-decoration: none; }

.boldbodycopy3 {font-family: Arial, Helvetica, sans-serif; font-size: 12px; line-height: 14px; font-weight: normal !important; color: #666666 ; text-decoration: none; }

html, body {
  background: none !important;
  background: #ffffcc !important;
}

}

It seems that you have to use the "!important" modifier in order for the changes to really work. Here is what the text in the original document looks like after these changes:

Thursday, April 1, 2010

Composite Annotations

This is draft for comment of a project coin proposal to allow language level composite annotations to the JDK. This should make it easier for annotation rich frameworks to provide stereotypes that represent common combinations. This came out of a discussion on the atmosphere mailing list.

PROJECT COIN SMALL LANGUAGE CHANGE PROPOSAL FORM v1.0

AUTHOR(S): Gerard Davison

OVERVIEW

Provide a two sentence or shorter description of these five aspects of the feature:

FEATURE SUMMARY: Should be suitable as a summary in a language tutorial.

Add the ability to compose existing annotations as meta annotations to be able to easily create stereotypes for common combinations.

MAJOR ADVANTAGE: What makes the proposal a favorable change?

Libraries can provide common "stereotype" made up of sensible default combinations of annotations.

MAJOR BENEFIT: Why is the platform better if the proposal is adopted?

Potentially shallower learning curve for annotation based frameworks.

MAJOR DISADVANTAGE: There is always a cost.

It is possible that by hiding configuration behind stereotypes that the code becomes harder to diagnose. This can be ameliorated to some extent with tool support and suitable naming conventions.

ALTERNATIVES: Can the benefits and advantages be had some way without a language change?

Yes, it is possible for each and every framework to introduce there own Composite marker annotation and processor. For example spring has something quite similar in there meta annotations:

http://blog.springsource.com/2009/05/06/spring-framework-30-m3-released/

Each implementation would be different then as not as easily accessible as a language feature would be.

EXAMPLES Show us the code!

SIMPLE EXAMPLE: Show the simplest possible program utilizing the new feature.


package java.lang.annotation;

@Rentention(SOURCE)
@Target(ANNOTATION_TYPE)
public @interface Composite
{
}


package x;

@Composite
@SuppressWarnings({"unchecked"})
@Target(METHOD)
public @interface SuppressUnchecked
{
}

package y;

public class ExampleService
{
   @SupressUnchecked
   public void methodWithOddCast()
   {
       ...
   }
}

ADVANCED EXAMPLE: Show advanced usage(s) of the feature.

@Composite
@WebService
@Binding(SOAPBinding.SOAP_12_BINDING)
@Addressing
@Target(CLASS)
public @interface SOAP12AddressingWebService
{
}

@SOAP12AddressingWebService
public class ExampleService
{
   ...
}

DETAILS SPECIFICATION: Describe how the proposal affects the grammar, type system, and meaning of expressions and statements in the Java Programming Language as well as any other known impacts.

The lexical grammar is unchanged. The type system is unchanged. The annotation type section is modified (JLS ?.?) so that an annotation can be applied to composite annotation if that annotation is tagged with @Composite and the target and retention matches that of the composite annotation. This prevents all annotations having to be modified with the ANNOTATION_TYPE modifier.

COMPILATION: How would the feature be compiled to class files?

This feature modifies the process which is used to gather the annotation properties. In general the rule is that values directly applied to class will override values provided by composite annotations. So the process for building the values for a particular class should be:

  1. For each annotation attached to the class that isn't marked as @Composite store the values for this class. Once defined the value will not change.
  2. For each annotation attached to the class in source order that is marked as @Composite apply any values that have not been previously defined. Recursively apply the values for any attached composite annotations.

The compilation should fail if there is a loop of @Composite annotations. (QUERY should we allow multi-level or is one turtle enough.)

For a client reading the class using the reflective API it should appear as if the annotations provided by the composite annotations were applied to the class directly. (QUERY should the composite annotations be erased at runtime?)

TESTING: How can the feature be tested?

It should be a matter of generating various simple cases with differing levels of composite annotations. Corner cases should be provided with inconsistent target or retention policies.

LIBRARY SUPPORT: Are any supporting libraries needed for the feature?

REFLECTIVE APIS: Do any of the various and sundry reflection APIs need to be updated? This list of reflective APIs includes but is not limited to core reflection (java.lang.Class and java.lang.reflect.*), javax.lang.model.*, the doclet API, and JPDA.

As the annotations are unwound at compile time this won't affect any reflective API that works from a class file. It is going to pose a question of what is the correct thing to do when looking at a source file. (QUERY not sure what the best thing is to do)

OTHER CHANGES: Do any other parts of the platform need be updated too? Possibilities include but are not limited to JNI, serialization, and output of the javadoc tool.

Yes, the javadoc tool should show both the composite and the applied annotations in some way.

MIGRATION: Sketch how a code base could be converted, manually or automatically, to use the new feature.

Roll up some application to use stereotypes as applicable.

COMPATIBILITY BREAKING CHANGES: Are any previously valid programs now invalid? If so, list one.

All existing programs remain valid.

EXISTING PROGRAMS: How do source and class files of earlier platform versions interact with the feature? Can any new overloadings occur? Can any new overriding occur?

The semantics of existing class files and legal source files and are unchanged by this feature.

REFERENCES EXISTING BUGS: Please include a list of any existing Sun bug ids related to this proposal.

None

URL FOR PROTOTYPE (optional):

No prototype at this time.

Thursday, March 11, 2010

Jersey 1.1.5.1 released

There is a minor point release of Jersey that includes a pair of significant bug fixes for weblogic and JDeveloper users. Worth using in preference to 1.1.5. Thanks a lot to Paul for putting this one together.

Tuesday, March 2, 2010

Normal comment service is now resumed

I have been fairly remiss in responding to comments, I have just worked my way though the backlog and now up to date. yay

Building and testing Jersey

So I was playing with a patch Building an testing to Jersey but I was having trouble building and running the tests. It seems that there are problems if you don't have the same specific version of Maven, (2.0.9), once that is solved you might run across another issue as the default memory given to Maven will not be enough for all the test to succeed.

The way to track the correct values is to take a look at "hudson/jersey.sh" and this will set the MAVEN_OPTS to:

export MAVEN_OPTS="-Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m"

This it turns out isn't sufficient if you are running behind a firewall, at least on Linux, and you need to specify the web proxy as well for some tests:

export MAVEN_OPTS="-Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m -Dhttp.proxyHost=proxy -Dhttp.noProxyHosts=localhost"

With that resolved I can build and test all of Jersey in a relatively small amount of time. Although it does seem you need an active network connection which is a shame.

Monday, February 1, 2010

iPad fashion dilemma

We had a debate in the office about how the apple iPad would affect male fashion choices. You had to store it somewhere on your person and is just too big for a normal pocket. I don't see man-bags becoming that fashionable so we had to think out of the box. Jon Russell our local artist, talented cartoonist and now fashion designer took inspiration from both Celtic and Rustic trad to produce his new iPad supporting range....

iPad-ta-noo and the iPad-bubba

I do hope that Steve Jobs approves of the new look for apple users '10/'11

Wednesday, January 27, 2010

A little bit of REST with Oracle ADF / SDO

So I have been working on my BuildAnApp project, that I have mentioned previously, and decided to try and take the day out to build a simple RESTful interface to my ADF application module. Just a simple resource so I can create and update items from a single table.

The problem is always how to convert you objects into XML, so I decided to expose the Application Module with a Service interface. This results in an set of methods that can update the model in terms of SDO object which are easy to convert into XML.

I then created another project in JDeveloper along with a class called Root configured as a JAX-RS resource, you can find more about working with Jersey / JAX-RS web services in the help for 11R1PS1. I won't go into details here.

One of the limitations of Jersey is that it isn't yet properly integrated in to the container so nice JEE annotations such as @Resource don't work properly. I blogged about this previously but you can just create a class that looks like this as a short cut:

package org.concept.model.rest;

import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.spi.container.WebApplication;
import com.sun.jersey.spi.container.servlet.ServletContainer;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;

import java.lang.reflect.Type;

import javax.annotation.Resource;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import javax.servlet.ServletConfig;


public class InjectingAdapter extends ServletContainer {
    @Override
    protected void configure(ServletConfig servletConfig, ResourceConfig rc,
                             WebApplication wa) {
        super.configure(servletConfig, rc, wa);


        rc.getSingletons().add(new InjectableProvider<Resource, Type>() {

                public ComponentScope getScope() {
                    return ComponentScope.PerRequest;
                }

                public Injectable<Object> getInjectable(ComponentContext ic,
                                                        Resource r, Type c) {

                    final String name = r.name();


                    return new Injectable<Object>() {

                        public Object getValue() {
                            
                            Object value = null;

                            try {
                                Context ctx = new InitialContext();
        

                                // Look up a data source
                                try {
                                    value = ctx.lookup(name);
                                } catch (NamingException ex) {

                                    value =
                                            ctx.lookup("java:comp/env/" + name);
                                }

                            } catch (Exception ex) {
                                ex.printStackTrace();
                            }

                            return value;
                        }
                    };
                }
            });
    }
}

You have to modify the web.xml that JDeveloper generates to use this new class rather than the default Jersey servlet. I have also added in a ejb-ref that will pick up the service interface from the application module.

<web-app>

  ...

  <servlet>
    <servlet-name>jersey</servlet-name>
    <servlet-class>org.concept.model.rest.InjectingAdapter</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>jersey</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
  <ejb-ref>
    <ejb-ref-name>ConceptServiceInterface</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <remote>org.concept.model.am.common.serviceinterface.ConceptModuleService</remote>
    <ejb-link>org.concept.model.am.common.ConceptModuleServiceBean</ejb-link>
  </ejb-ref>
</web-app>

The next thing you have to worry about is that Jersey doesn't know how to deal with SDO objects out of the box. So we need to provide a Writer and Reader, these can be placed on the class path and Jersey will pick them up. Note that the implementation is far from production quality; but it is enough to get started.

package org.concept.model.rest;


import commonj.sdo.DataObject;
import commonj.sdo.helper.XMLHelper;

import java.io.IOException;
import java.io.OutputStream;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;


@Produces("application/sdo+xml")
@Provider
public class SDOMessageBodyWriter implements MessageBodyWriter<DataObject> {
    public SDOMessageBodyWriter() {
        super();
    }

    public boolean isWriteable(Class c, Type type, Annotation[] annotation,
                               MediaType mediaType) {
        return true;
    }


    public long getSize(DataObject dataObject, Class<?> class1, Type type,
                        Annotation[] annotations, MediaType mediaType) {
        return -1L;
    }

    public void writeTo(DataObject dataObject, Class<?> class1, Type type,
                        Annotation[] annotations, MediaType mediaType,
                        MultivaluedMap<String, Object> multivaluedMap,
                        OutputStream outputStream)  {

        // From http://wiki.eclipse.org/EclipseLink/Examples/SDO/StaticAPI
        
        // and http://www.eclipse.org/eclipselink/api/1.1/index.html


        try {
            commonj.sdo.Type sdoType = dataObject.getType();
            XMLHelper.INSTANCE.save(dataObject, sdoType.getURI(), sdoType.getName(), outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

And the reader implementation:

package org.concept.model.rest;


import commonj.sdo.DataObject;
import commonj.sdo.helper.XMLDocument;
import commonj.sdo.helper.XMLHelper;

import java.io.InputStream;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;


@Consumes("application/sdo+xml")
@Provider
public class SDOMessageBodyReader implements MessageBodyReader<DataObject> {
    public boolean isReadable(Class<?> class1, Type type,
                              Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    public DataObject readFrom(Class<DataObject> class1, Type type,
                               Annotation[] annotations, MediaType mediaType,
                               MultivaluedMap<String, String> multivaluedMap,
                               InputStream inputStream) {
        
        try
        {
            XMLDocument xmldocument = XMLHelper.INSTANCE.load(inputStream);        
            DataObject dos = xmldocument.getRootObject();
            return dos;
        }
        catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }
}

Now for the root resource, and we provide methods to get a list of concepts in the form of a URIList, get the data for a particular Concept, and update the values for a concepts. Delete and create are left as an exercise for the reader.

package org.concept.model.rest;


import commonj.sdo.helper.DataFactory;

import java.math.BigDecimal;

import java.net.URI;

import java.util.List;

import javax.annotation.Resource;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import oracle.jbo.common.service.types.FindControl;
import oracle.jbo.common.service.types.FindCriteria;

import org.concept.model.am.common.serviceinterface.ConceptModuleService;
import org.concept.model.vo.common.ConceptViewSDO;


@Path("/")
public class Root {

  @Resource(name = "ConceptServiceInterface")
  private ConceptModuleService service;
 

  @GET
  @Path("/concepts/")
  @Produces("application/xml")
  public Response getConcepts(
      @Context UriInfo info,
      @QueryParam("start") Integer start,
      @QueryParam("size") Integer size) {
    
    // If we don't have the start and end then redirect with one otherwise
    // the client could get a lot of data back
    //
    if (size==null) {
      return Response.seeOther(
        info.getRequestUriBuilder().queryParam("start", start!=null?start : 0)
                                   .queryParam("size", 10).build()).build();
    }
    
    //

    FindCriteria criteriaImpl =
      (FindCriteria)DataFactory.INSTANCE.create(FindCriteria.class);
    criteriaImpl.setFetchStart(start);
    criteriaImpl.setFetchSize(size);
    
    
    FindControl controlImpl =
      (FindControl)DataFactory.INSTANCE.create(FindControl.class);
    List list =
      service.findConceptView(criteriaImpl, controlImpl);

    // Build the uri list to return to the client
    //
    
    URI uriList[] = new URI[list.size()];
    int i = uriList.length;
    for (int j = 0; j < i; j++) {
      uriList[j] = info.getBaseUriBuilder().path("concept").path(
        list.get(j).getConceptId().toString()).build();
    }

    return Response.ok(new URIList(uriList)).build();
  }

  

  @GET
  @Path("/concepts/{concept}")
  @Produces("application/sdo+xml")
  public ConceptViewSDO getConcept(@PathParam("concept")
    BigDecimal conceptId) {
    ConceptViewSDO conceptView = service.getConceptView(conceptId);
    return conceptView;
  }


  @PUT
  @Path("/concepts/{concept}")
  @Consumes("application/sdo+xml")
  public void updateConcept(
    @PathParam("concept")
        BigDecimal conceptId,
    ConceptViewSDO concept) {

    // Check we are updating the correct concept
    //
    if (!conceptId.toBigInteger().equals(concept.getConceptId())) {
      throw new WebApplicationException(
              Response.Status.CONFLICT);
    }
    
    service.updateConceptView(concept);
  }


}

Now there is a lot more I could do for this resource, for example next and previous links to make it easier to iterate over the list. In the query method we should modify the FindCriteria to only return the ID attributes for performance reasons. We should also be providing action links for operations that we wish to perform on the concept entity. Finally since SDO allows you to easily generate a change summary it would be nice to be able to support the new HTTP PATCH method.

As I work on the project I will update this page as I add more features. There is some interesting hypermedia stuff coming in future versions of Jersey and I hope to be able to update this example to take this into account.

Friday, January 8, 2010

JAX-WS Proxy client for a WSDL that container HTTP Binding rather than SOAP Binding

I came across a curios bug where the WSDL uses HTTP Binding rather than SOAP Binding. This caused all sort of hell with the proxy wizard in JDeveloper, which I am working on, but I still ran into problems when I tried to run the client that was generated from the wsimport tool.

It turns out that the JAX-WS dynamic proxy client, the ones with the SEI, doesn't support HTTP/REST out of the box. In order to have this working you need to add both a binding feature and to set the endpoint address. So you code looks something like this:

    Port port = service.getMyPort(
        new BindingTypeFeature(JAWWSProperties.REST_BINDING));
    ((BindingProvider)port).getRequestContext(
        BindingProivider.ENDPOINT_ADDRESS_PROPERTY,
        "http://..../rest-like-endpoint");
    port.doSomethingInteresting(...);

Not something you will come across every day; but worth knowing all the same.