Thursday, December 31, 2009

Having problems with WS-RM again

I'm currently using Netbeans 6.7.1 with Glassfish v2.1. This Glassfish version is bundled with Metro 1.1.5. When trying to use WS-RM with these stuff, you can really easily run into problems.

If you set up a simple web project with a simple webservice, and enable Reliable Messaging (also enabling Delivering Messages In Exact Order), you get the stuff working right. You can see the added SOAP headers in the WS Tester page, and when monitoring Metro HTTP transfer (com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true ), you can see that there are actually two request-response pairs.

But if you want to set up this simple scenario having EJBs included, it just won't work. Create a new EE project, add a stateless session bean with a dummy method, add a webservice on this bean, and enable RM and Order on it. I get the following exception in this case:

java.lang.NullPointerException
        at com.sun.ejb.containers.WebServiceInvocationHandler.invoke(WebServiceInvocationHandler.java:198)
        at $Proxy96.greet(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at com.sun.enterprise.webservice.InvokerImpl.invoke(InvokerImpl.java:78)
        at com.sun.enterprise.webservice.EjbInvokerImpl.invoke(EjbInvokerImpl.java:82)
        at com.sun.xml.ws.server.InvokerTube$2.invoke(InvokerTube.java:146)
        at com.sun.xml.ws.server.sei.EndpointMethodHandler.invoke(EndpointMethodHandler.java:257)
        at com.sun.xml.ws.server.sei.SEIInvokerTube.processRequest(SEIInvokerTube.java:93)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:595)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:554)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:539)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:436)
        at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:106)
        at com.sun.enterprise.webservice.MonitoringPipe.process(MonitoringPipe.java:147)
        at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:115)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:595)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:554)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:539)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:436)
        at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:106)
        at com.sun.xml.ws.tx.service.TxServerPipe.process(TxServerPipe.java:317)
        at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:115)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:595)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:554)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:539)
        at com.sun.xml.ws.api.pipe.Fiber.run(Fiber.java:388)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:619)

In the current project, we yet have to stick to Metro version 1.1.5. Altought, I tried to set up the same configuration with Metro 1.5, and it worked fine.

But actually, you can get things working fine with Metro 1.1.5 as well. If you change the webservices libraries to the 1.5 implementation in Glassfish (as mentioned here: http://forums.netbeans.org/post-36125.html ), Netbeans will use .NET 3.5 / Metro 1.3 compatibility mode, and it will put WS-RM Policy 1.2 compliant stuff in the WSIT xml (xmlns:wsrm="http://docs.oasis-open.org/ws-rx/wsrmp/200702" ) However, with Metro 1.1.5, it uses .NET 3.0 / Metro 1.0 mode, it will tend to use WS-RM 1.1 (xmlns:wsrm="http://schemas.xmlsoap.org/ws/2005/02/rm/policy" ). But actually Metro 1.1.5 supports WS-RM 1.2 and WS Policy 1.5 (http://www.w3.org/ns/ws-policy ) - you can find it in the source code clearly.

The problem seems to be not only with Metro 1.1.5 and WS-RM 1.1, but also with Metro 1.5 and WS-RM 1.1. In such situation, I get this exception:

java.lang.NullPointerException
        at com.sun.enterprise.webservice.SOAPMessageContextImpl.getMessage(SOAPMessageContextImpl.java:86)
        at com.sun.enterprise.webservice.monitoring.MessageTraceImpl.setMessageContext(MessageTraceImpl.java:124)
        at com.sun.enterprise.webservice.monitoring.JAXWSEndpointImpl.processResponse(JAXWSEndpointImpl.java:105)
        at com.sun.enterprise.webservice.MonitoringPipe.process(MonitoringPipe.java:155)
        at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:115)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:598)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:557)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:542)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:439)
        at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:112)
        at com.sun.xml.ws.tx.service.TxServerPipe.process(TxServerPipe.java:303)
        at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:115)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:598)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:557)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:542)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:439)
        at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:112)
        at com.sun.enterprise.webservice.CommonServerSecurityPipe.processRequest(CommonServerSecurityPipe.java:222)
        at com.sun.enterprise.webservice.CommonServerSecurityPipe.process(CommonServerSecurityPipe.java:133)
        at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:115)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:598)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:557)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:542)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:439)
        at com.sun.xml.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:243)
        at com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:471)
        at com.sun.xml.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:244)
        at com.sun.xml.ws.transport.http.servlet.ServletAdapter.handle(ServletAdapter.java:135)
        at com.sun.enterprise.webservice.Ejb3MessageDispatcher.handlePost(Ejb3MessageDispatcher.java:113)
        at com.sun.enterprise.webservice.Ejb3MessageDispatcher.invoke(Ejb3MessageDispatcher.java:87)
        at com.sun.enterprise.webservice.EjbWebServiceServlet.dispatchToEjbEndpoint(EjbWebServiceServlet.java:228)
        at com.sun.enterprise.webservice.EjbWebServiceServlet.service(EjbWebServiceServlet.java:157)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
        at com.sun.enterprise.web.AdHocContextValve.invoke(AdHocContextValve.java:114)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:87)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:222)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:166)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)
        at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:288)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:647)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:579)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:831)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:341)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:263)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:214)
        at com.sun.enterprise.web.portunif.PortUnificationPipeline$PUTask.doTask(PortUnificationPipeline.java:380)
        at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:265)
        at com.sun.enterprise.web.connector.grizzly.ssl.SSLWorkerThread.run(SSLWorkerThread.java:106)

Such exception can be found in two reported bugs: https://glassfish.dev.java.net/issues/show_bug.cgi?id=7981 , and http://netbeans.org/bugzilla/show_bug.cgi?id=163210

Quite a mess. So - if you can switch to a newer Metro implementation, do it. Otherwise, just change the declarations in your wsit-*.xml (replace those generated by netbeans) to the WS-RM 1.2 and WSP 1.5 ones, and it should work fine.

Wednesday, November 25, 2009

Metro WS-AT transaction coordination and https

I recently had to configure a secure and reliable webservice connection. We use WS-AtomicTransactions, WS-Security (Mutual Certificates Security, MCS) and WS-ReliableMessaging, however all this over HTTP transport (not HTTPS). Things are quite easy to set up with Netbeans (6.7.1) and Glassfish v2.1 (which uses Metro 1.1.5). But at some point it is just not working, and you have no more buttons to click in the wizard windows.

The situation was simple, I used two different projects to pilot the solution. Both had a simple EJB Session bean with a method, also published as webservice operation. From one of the EJBs I invoked the other with WS (this connection had AT, RM and Security configured). I tested the first WS with its standard Tester. The two applications got deployed on two different glassfish domains, just to make sure.

The problem came when I enabled transactions:

WSTX-COORDINATOR-3006: register sent to EPR '
https://hhcofcmds:8281/__wstx-services/wscoor/coordinator /register
7ef29c5a- 2633-40a7-ae4a-54d94e818ec0
' failed for activityId 'uuid:WSCOOR-SUN-3871ef38-e5b2-490c- 9fbe-a39fa442daee'. Nested exception: 'HTTP transport error: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target' WSTX-AT-0022: Registration with durable parent failed: ' coordId=uuid:WSCOOR- SUN-3871ef38-e5b2-490c-9fbe-a39fa442daee partId:1 '

Some forum entries suggest that I should disable transactions on the invoked WS. Well, this is not an option for me, as having transactions was my actual intent. So, WS-AT and WS-RM use some additional WS invocations to the transaction coordinator service to ensure all the stuff it has to ensure. The logs above show that someone tries to contact the coordinator over https. Which surely won't be wokring, as I use http transfer for the whole stuff.

I found a note in some forum post that the coordination stuff uses https by default. So I dived into the source to find this in class com.sun.xml.ws.tx.common.AddressManager


    private static String preferredScheme = 
System.getProperty("com.sun.xml.ws.tx.preferredScheme", "https");

The methods getPreferredAddress and getAddress use this setting. So we just have to add this line in the proper place in the domain.xml of both glassfish domains:

        <jvm-options>-Dcom.sun.xml.ws.tx.preferredScheme=http</jvm-options>

This will cause the transaction coordination to use http transfer.

Wednesday, October 14, 2009

A tricky thing about generics

I ran into this a couple of times now. When using numbers as keys in maps, you have to be really careful with number literals. Number literals by default compile to be of int primitive type and they can be automatically cast (boxed) to Object as Integers.

Take the code below:

public class Test {

 static Map testMap = new HashMap();
 
 public static void main(String[] args) {
  testMap.put((long)0, "zero");
  System.out.println(testMap.get((long)0));
  System.out.println(testMap.get(0));
 }
}

Notice the cast to long primitive type, tesMap.put(0, "zero") won't even compile. The unfortunate thing is only that the java.util.Map.get method is not parametrized with the Key class of the Map, so testMap.get(0) compiles without errors, but it returns null.

Friday, May 22, 2009

Using GWT and Ajax4jsf together

Recently I had to face a situation it's hard to speak about :) In a Seam+RichFaces based application we needed to develop a special component, which needs a large amount of client side JavaScript code. I decided to use GWT to develop this functionality. It's very simple to embed a GWT application supporting a JSF component to a page, especially if you have only one instance of the component on the page - you just include the compiled JS.

Everything seemed working fine until I tested it in Internet Explorer 6. In IE6, the GWT RPC calls just didn't succeed, the IE6 JavaScript error report said 'unhandled exception', in a meaningless line of JS code, of course. I found that it works fine if I don't have an a4j:form on my page. After a number of tries I found that the loading of the core JavaScript library of Ajax4Jsf already causes the problem, and then, looking through the code, I found a suspicious statement:

 XMLHttpRequest = function() {
  if (!_SARISSA_XMLHTTP_PROGID) {
   _SARISSA_XMLHTTP_PROGID = Sarissa.pickRecentProgID( [
     "Msxml2.XMLHTTP.6.0", "MSXML2.XMLHTTP.3.0",
     "MSXML2.XMLHTTP", "Microsoft.XMLHTTP" ]);
  }
  return new ActiveXObject(_SARISSA_XMLHTTP_PROGID);
 };

And this turned out to be the problem indeed. Normally, the XMLHttpRequest variable is simply null. I didn't dare look through the GWT RPC implementation to investigate the problem further :) But at this point, we can already work the thing around.

So I introduced a 'beforeRequest' and 'afterRequest' JSNI methods in my GWT code to ensure that '$wnd.XMLHttpRequest' is null during the RPC call. This way, GWT RPC calls work fine, and the normal a4j functionality is still available in the page.

Tuesday, May 12, 2009

Showing not shown Faces Messages

Most of us using JSF 1.2 RI are familiar with the warning message in our logs:

[lifecycle] WARNING: FacesMessage(s) have been enqueued, but may not have been displayed.

JavaServer Faces has a mechanism to assign messages to specific components. In the common case, if an input field has a validation error, the error message is queued to that field, and a h:message put next to the input field can show the message on the page:

<h:inputtext id="firstName"/>
<h:message for="firstName"/>

The JSF Reference Implementation has a solution to help us find out if we forgot to put a h:message tag for some components. Unfortunately, it only shows a warning message in the log, and we have no programmatic way to process those messages. Well, almost no way.

If you dare take a look in com.sun.faces.lifecycle.RenderResponsePhase, you can see that if there are any messages queued, a special variable of the Set is added to the request map. All client ids of controls with faces messages are added to this set. When h:message tags render, they access the messages queued for their associated client ids using the getMessages(String clientId) method in com.sun.faces.context.FacesContextImpl. This method removes the client id from the set, thus signalling that the messages for that client id are displayed. When finished rendering, messages for the client ids remaining in the Set are those that are not displayed. The class RenderResponsePhase simply logs these messages and then disposes the Set.

In one of my applications, I wanted to present these messages at the top of my forms. This can come really useful especially in development phase. As seen from the above, I have the necessary information ready at the very end of each render phase. I created a Seam component with a method to handle it:

@Name("messageHandler")
public class MessageHandler {

public String handleUnAssignedMessages() {
    // copy-paste from com.sun.faces.lifecycle.RenderResponsePhase
        Set clientIds = TypedCollections.dynamicallyCastSet(
                (Set) FacesContext.getCurrentInstance().getExternalContext().getRequestMap()
                .remove(RIConstants.CLIENT_ID_MESSAGES_NOT_DISPLAYED), String.class);
        if (clientIds != null)
            for (String clientId : clientIds) {
                Iterator messages = 
                    FacesContext.getCurrentInstance().getMessages(clientId);
                while (messages.hasNext()) {
                    FacesMessage message = messages.next();
                    if (message.getSeverity() == FacesMessage.SEVERITY_ERROR ||
                            message.getSeverity() == FacesMessage.SEVERITY_FATAL) {
                        String text = message.getSummary() + " " + message.getDetail();
                        FacesContext.getCurrentInstance().addMessage(null,
                                new FacesMessage(FacesMessage.SEVERITY_ERROR, text, text));
                    }
                }
            }
    return "";
}
}

This method simply collects all nondisplayed error messages and enqueues them without assigning them to a specific client id. Note that you'll have to include the jsf-impl.jar in your classpath for this code to compile. Having these messages enqueued, you can present them by a h:messages tag, or you can do anything programmatically with them.

Note that as you only have this information at the end of the render phase, you have to put the invocation of this method at the end of your page. If you want also to present them on the page with a h:messages tag, you have to put it after the invocation of the method above. You can then move the html output to the desired place in your document (above your forms, probably) with a simple javascript code:

#{messageHandler.handleUnAssignedMessages()} <h:messages layout="table" styleclass="msg_gl" infoclass="info-message" warnclass="warning-message" errorclass="error-message" globalonly="#{true}" showsummary="#{false}" showdetail="#{true}"> </h:messages>

So that's it.

Tuesday, April 28, 2009

Experiences with AppEngine

Well, I was quite excited to get my Java early look request accepted. I'm publishing an application soon (I'll write about it later). After the initial success with the "hello world" application I soon had to face some problems.

At my first test project, I started to work with JPA. The list of unsupported features in the docs didn't scare me much. But then found more. First of all, for Id generation, only Identity generation type is supported, but that's a minor issue. A bigger problem was that I could not persist multiple entities in one EntityManager session. This might have been solved by some configuration on Datanucleus, with which I am not familiar, and which I did not start to search for. Finally I just did not manage to persist an associated entity. I got exceptions stating that the parent entity is already persisted with another Id, however the parent entity class mentioned by the exception was actually the child entity. Anyone more familiar with Datanucleus can surely get further.

Then I started to use the JDO framework. I soon ran into the exception mentioned also here, but as there said, it just got resolved at some point, I don't know why.

I had the most problems with my web framework. I'm using an own experimental Direct Ajax framework, called Daam, which is originally designed to work integrated with JBoss Seam, but with some modifications it could be used in the AppEngine environment. A serious thing to consider was that I really have to store all my application information manually in the http session context, and I guess I cannot assume that the same servlet instance will handle all requests of a session. These things are handled by JBoss Seam in the original architecture.

The problems came when I deployed in online. First of all, java.lang.reflect.Method is not serializable, but the deserialization of some objects can be unsuccessful. The exception is similar to the one mentioned in the issue 1290, but I had it with any Object [] or String [] arrays.

Nested in javax.servlet.ServletException: [Ljava.lang.String;:
java.lang.ArrayStoreException: [Ljava.lang.String;
 at java.io.ObjectInputStream.readArray(Unknown Source)
       [...]
       at com.google.apphosting.runtime.jetty.SessionManager.deserialize(SessionManager.java:358)

It also seems that SimpleDateFormat also contains arrays. I had to turn my array fields into Lists and I had to declare my DateFormat fields transient and reinitiate them on need. Later I also found this post. That's it for now, I hope it will help some.