Thursday, 28 May 2009

OTN Article - Taking an Oracle ADF Application from Design to Reality

I'm happy to announce that a 5 part article Taking an Oracle ADF Application from Design to Reality authored by Penny Cookson and myself has been published on Oracle's Technology Network.

I'd like to thank Penny for her assistance in writing the article, Steve Muench and John Stegeman for their ideas (supplied directly and indirectly), Grant Ronald for his review and Justin Kestelyn's OTN team for their hard work.

Wednesday, 27 May 2009

Java Developer Journal - JDeveloper - an IDE that moves with the times

I'm happy to announce that an article of mine has recently been published in the Java Developer Journal, titled "JDeveloper - an IDE that moves with the times". You can pick up the full JDJ magazine here or the online article here.

Thanks to Teri Whitaker and Shay Shmeltzer for their assistance with this article.

Tuesday, 26 May 2009

JDeveloper next version - new features sneak preview

Jakub Pawlowski has spotted the beta documentation for the next release of JDev, great work Jakub. Best to check it out now, as the URL indicates this is a beta release and the URL may not be there for long.

As Jakub has pointed out, there's a great new calendar component arriving. I remember asking for something like this a year or so ago, and the Oracle Gods have delivered. Great work :-)

Some other things I spotted from a quick scan:

1) Dashboard component

2) Hierarchy Viewer

3) ADF BC - Integrating Service-Enabled Application Modules

4) Whole new manual Desktop Integration Developer's Guide

5) Manual for potentially a new product Oracle Help (at least I hadn't heard of it before)

...and so on... it's worth a stroll through the documentation to check out new features. I'd appreciate if you find any more, to post them as a comment on this blog post please.

Sunday, 10 May 2009

I'm happy to announce...

I'm happy to announce the arrival of Jessica Muir, a calm and collected little 3.5 kilo girl. Mum and Jessica are doing just fine, while Dad is running a quick "education" campaign with Jessica's 2yr old sister about the merits of little sisters.


Our new arrival spells a few weeks break from blogging, normal services will resume shortly.

Wednesday, 6 May 2009

JAX-WS: Throwing generic SOAPFaults under WLS 10.3

(With apologies I accidentally deleted this post. This is a repost).

Typically thanks to the WSDL operations including a <soap:fault> element, when generating JAX-WS 2.1.x endpoints from the WSDL, the JAX-WS engine will also create @WebFault classes to mirror the <soap:fault>. As such the programmer can throw the @WebFault class to cause a SOAP fault to be returned to the caller.

However what if the WSDL operation doesn't include an explicitly defined fault for the operation? How does the programmer throw a fault without a @WebFault class? The simple answer is add it to the WSDL and regenerate, but some of us don't have that luxury, the WSDLs are given to us.

If you're using SOAP 1.1 the following code will allow you throw a generic SOAP fault from your JAX-WS endpoint:

SOAPFactory fac = SOAPFactory.newInstance();
SOAPFault sf = fac.createFault("Your error message", new QName("http://schemas.xmlsoap.org/soap/envelope/", "Client"));
throw new SOAPFaultException(sf);
Note the QName uses the SOAP 1.1 namespace and "Client" fault code.

The SOAP 1.2 equivalent code:
 
SOAPFactory fac = SOAPFactory.newInstance();
SOAPFault sf = fac.createFault("Your error message", new QName("http://www.w3.org/2003/05/soap-envelope", " Receiver"));
throw new SOAPFaultException(sf);

In this example the QName uses the SOAP 1.2 namespace and "Receiver" fault code.

(Later edit – for the record both these mechanisms can also be used in a custom JAX-WS protocol (SOAP handler) or logical handler).

In the SOAP 1.2 case you'll see something like the following SOAP fault returned from the web service call:
 




S:Receiver


Your error message



javax.xml.ws.soap.SOAPFaultException: Your error message








Specifically for Oracle's WebLogic Server (WLS) v10.3, we need to make a slight change to the SOAP 1.2 code by supplying the specific SOAP protocol to the SOAPFactory.newInstance call:

SOAPFactory fac = SOAPFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
SOAPFault sf = fac.createFault("Your error message", new QName("http://www.w3.org/2003/05/soap-envelope", " Receiver"));
throw new SOAPFaultException(sf);
If you don't do this the web service will return an empty HTTP body (though the HTTP header will include a "500 internal server error"), and you'll see the following Java Stack Trace:

java.lang.RuntimeException: javax.xml.ws.soap.SOAPFaultException: Your error message

6/05/2009 10:18:23 com.sun.xml.internal.messaging.saaj.soap.ver1_1.Fault1_1Impl getFaultSubcodes
SEVERE: SAAJ0303: Operation getFaultSubcodes not supported by SOAP 1.1
6/05/2009 10:18:23 com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit handle
SEVERE: Not supported in SOAP 1.1
java.lang.UnsupportedOperationException: Not supported in SOAP 1.1
at com.sun.xml.internal.messaging.saaj.soap.ver1_1.Fault1_1Impl.getFaultSubcodes(Fault1_1Impl.java:220)
at com.sun.xml.ws.fault.SOAPFaultBuilder.createSOAP12Fault(SOAPFaultBuilder.java:404)
at com.sun.xml.ws.fault.SOAPFaultBuilder.createSOAPFaultMessage(SOAPFaultBuilder.java:172)
at com.sun.xml.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:249)
at com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:444)
at com.sun.xml.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:244)
at com.sun.xml.ws.transport.http.servlet.ServletAdapter.handle(ServletAdapter.java:134)
at weblogic.wsee.jaxws.HttpServletAdapter$AuthorizedInvoke.run(HttpServletAdapter.java:272)
at weblogic.wsee.jaxws.HttpServletAdapter.post(HttpServletAdapter.java:185)
at weblogic.wsee.jaxws.JAXWSServlet.doPost(JAXWSServlet.java:180)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at weblogic.wsee.jaxws.JAXWSServlet.service(JAXWSServlet.java:64)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:292)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:175)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3498)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
at weblogic.security.service.SecurityManager.runAs(Unknown Source)
at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2180)
at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2086)
at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1406)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)
(I don't usually include the complete stack trace in a blog post, but in this case the error is so obscure, it'll help others with Google searches)

I'm currently unclear on why this occurs under WLS. Having tested the original code on Netbeans 6.5 & Glassfish 2, it doesn't have the same issue. I note WLS 10.3 uses JAX-WS 2.1.3 and Glassfish 2 uses JAX-WS 2.1.4, so it may be a simple fix in the later JAX-WS release.

Thanks very much to Gerard Davison from Oracle UK for his assistance on resolving this one.

Tuesday, 5 May 2009

JAX-WS: A @SchemaValidation custom handler to alter framework SOAP faults

A few days ago I blogged about suppressing the SOAP fault detail Java stack trace on WebLogic Server when using the JAX-WS @SchemaValidation annotation for your web services.

One of the problems with the @SchemaValidation annotation is dependent on the error in your incoming SOAP payload, you may get all sorts of potential errors returned in a SOAP fault. In some cases it might be useful or a requirement that a generic SOAP fault message be sent back in this case. How to do this?

As usual I'm documenting my research and solution here for my own purposes, but hopefully helpful to others. Your mileage may vary.

Consider the following JAX-WS endpoint:


@WebService
@SchemaValidation
public class WebServiceClass {

@WebMethod
public MyResponse lodgeReport(@WebParam MyRequest request)
throws GenericSoapFault {
// do some stuff
}
}


(I've clipped a lot of the detail from above to focus on the specifics)

Notice the @SchemaValidation annotation. By default this will enforce that the incoming SOAP payload conforms to the XML schema for the operation. For instance an invalid payload may throw the following error:






S:Receiver


org.xml.sax.SAXParseException: cvc-datatype-valid.1.2.1: '?' is not a valid value for 'dateTime'.






Note the reason/text part of the SOAP fault may be virtually any SAXParseException error message depending on the error discovered in the incoming SOAP payload.

We can however override the @SchemaValidation annotation to specify the actual error handler. We change the endpoint code as such:


@WebService
@SchemaValidation(handler = SchemaValidationErrorHandler.class)
public class WebServiceClass {

@WebMethod
public MyResponse lodgeReport(@WebParam MyRequest request)
throws GenericSoapFault {
// do some stuff
}
}


Note the handler attribute specifying the SchemaValidationErrorHandler class for the @SchemaValidation annotation. In turn the SchemaValidationErrorHandler is implemented as follows:


import com.sun.xml.ws.developer.ValidationErrorHandler;
import org.xml.sax.SAXParseException;

public class SchemaValidationErrorHandler extends ValidationErrorHandler {

public void warning(SAXParseException exception) { }

public void error(SAXParseException exception) { }

public void fatalError(SAXParseException exception) { }
}


Any warning, error or fatal error for the @SchemaValidation will now be passed through to the methods above. Remembering our goal to supplement a custom error message for any of the errors above (well, maybe not warning, we'll ignore warnings), we'd like to return our own SOAP fault. Unfortunately we can't do that from the handler, we need to pass the error back to the endpoint method. This is done by using the com.sun.xml.ws.api.message.Packet that is available from the ValidationErrorHandler super class. In essence we can change our handler class as follows:


public class SchemaValidationErrorHandler extends ValidationErrorHandler {

public static final String WARNING = "SchemaValidationWarning";
public static final String ERROR = "SchemaValidationError";
public static final String FATAL_ERROR = "SchemaValidationFatalError";

public void warning(SAXParseException exception) {
packet.invocationProperties.put(WARNING, exception);
}

public void error(SAXParseException exception) {
packet.invocationProperties.put(ERROR, exception);
}

public void fatalError(SAXParseException exception) {
packet.invocationProperties.put(FATAL_ERROR, exception);
}
}


I can't for the life of me find the JavaDoc for the Packet class, but what I believe it does is wrap the overall web service message context, and we can drop properties into the packet to be retrieved elsewhere by our JAX-WS solution , specifically our end point.

Returning to the JAX-WS endpoint we need to inject the web service message context, and then we can retrieve each of the individual errors from the SchemaValidationErrorHandler above:


@WebService
@SchemaValidation(handler = SchemaValidationErrorHandler.class)
public class WebServiceClass {

@Resource
WebServiceContext wsContext;

@WebMethod
public MyResponse lodgeReport(@WebParam MyRequest request)
throws GenericSoapFault {

MessageContext messageContext = wsContext.getMessageContext();
// We'll ignore warnings
// SAXParseException warningException = (SAXParseException)messageContext.get(SchemaValidationErrorHandler.WARNING);
SAXParseException errorException = (SAXParseException)messageContext.get(SchemaValidationErrorHandler.ERROR);
SAXParseException fatalErrorException = (SAXParseException)messageContext.get(SchemaValidationErrorHandler.FATAL_ERROR);

String errorMessage = null;

if (errorException != null)
errorMessage = errorException.getMessage();
else if (fatalErrorException != null)
errorMessage = fatalErrorException.getMessage();


if (errorMessage != null)
throw new GenericSoapFault(errorMessage);

// Otherwise process the request
}
}


Note the @Resource annotation in the class injecting the web service context, then within our endpoint method fetching the MessageContext from the wsContext, which gives us the ability to retrieve the properties we inserted in the SchemaValidationErrorHandler class.

As per the solution above, instead of just checking if the errorMessage is null and passing that to the GenericSoapFault, you could instead replace the SAXParseException with a more generic error message.