Thursday, February 19, 2009

Exposing Tables Mapped to Entity Beans as a Webservice

I wanted to create a simple web service to access some database tables. This turns out to be a little bit harder than I though as the classes generated for JPA are a little bit different in intention as those required JAXB / Web Services which are more method driven. This example just uses EJB; but the same should apply to using JPA straight.

Although the screen grabs are from JDeveloper, of course, the the following should apply to most java IDEs. So first of all lets create some entities from tables in the database:

Then I am going to create a connection to the old favourite scott/tiger:

Now in the screen shot and a few more I have selected more than just Emp/Dept. Just ignore that and assume that I had just selected the two. I didn't have the heart to go back and re-shoot them.

So finish the entity bean wizard and then bring up the EJB Sesssion Bean wizard so we can create a suitable Fa├žade to publish as our web service.

You can pretty much accept the defaults on this page as it has already picked up the persistence unit from the previous wizard:

Now we can just clagg on the @WebService annotation and run the service....

... well it turns out that if you try to run the Session Bean that it fails to deploy to the server with something like the following error:


[HTTP:101216]Servlet: "WSEE_SERVLET" failed to preload on startup in Web application: "/EmpDept-EmpDeptFacade-webapp".
javax.xml.ws.WebServiceException: Unable to create JAXBContext
 at com.sun.xml.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:158)
 ...
 at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)
Caused by: java.security.PrivilegedActionException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.sql.Timestamp does not have a no-arg default constructor.
 this problem is related to the following location:
  at java.sql.Timestamp
  at public java.sql.Timestamp empdeptfacade.Emp.getHiredate()
  at empdeptfacade.Emp
  at public java.util.List empdeptfacade.jaxws.QueryEmpFindAllResponse._return
  at empdeptfacade.jaxws.QueryEmpFindAllResponse

 at java.security.AccessController.doPrivileged(Native Method)
 at com.sun.xml.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:148)
 ... 54 more
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.sql.Timestamp does not have a no-arg default constructor.
 this problem is related to the following location:
  at java.sql.Timestamp
  at public java.sql.Timestamp empdeptfacade.Emp.getHiredate()
  at empdeptfacade.Emp
  at public java.util.List empdeptfacade.jaxws.QueryEmpFindAllResponse._return
  at empdeptfacade.jaxws.QueryEmpFindAllResponse

 at com.sun.xml.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:102)
 at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:438)
 at com.sun.xml.bind.v2.runtime.JAXBContextImpl.(JAXBContextImpl.java:286)
 at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:139)
 at com.sun.xml.bind.api.JAXBRIContext.newInstance(JAXBRIContext.java:105)
 at com.sun.xml.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:153)
 at com.sun.xml.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:148)
 ... 56 more

Now JAXB will handle quite a few cases/classes by default; but one annoying case it won't deal with is the java.sql.Timestamp class as it doesn't have a no-arg constructor. So work around this you need to define an adapter to do the conversion, here is a trivial implementation that may not work in all cases, particularly is you plan to write back to the database:

package empdeptfacade;

import java.sql.Timestamp;

import java.util.Date;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class TimestampAdapter
  extends XmlAdapter<Date, Timestamp> {

    public Date marshal(Timestamp v) {
        return new Date(v.getTime());
    }

    public Timestamp unmarshal(Date v) {
        return new Timestamp(
            v.getTime());
    }
}

Now you could attach this to each and every usage of the Timestamp class, admittedly just the once in this case, but a much tidier way to do it is to attach it to the package-info.java class instead and have it apply to a number of classes. Unfortunately JDeveloper doesn't allow you to create one from the java wizard - so you just have to use the "New File" wizard from the gallery.

And the code for the package needs to look like:


@XmlJavaTypeAdapters(
   @XmlJavaTypeAdapter(value=TimestampAdapter.class,type=Timestamp.class))
package empdeptfacade;

import java.sql.Timestamp;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;

You should now find that your EJB will deploy quite happily; but you will find that if you actually invoke one of the methods on the web service it fails with the following error:

avax.xml.ws.WebServiceException: javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: empdeptfacade.Dept@50dd8d -> empdeptfacade.Emp@11541d8 -> empdeptfacade.Dept@50dd8d]
 at com.sun.xml.ws.message.jaxb.JAXBMessage.writePayloadTo(JAXBMessage.java:322)
 at com.sun.xml.ws.message.AbstractMessageImpl.writeTo(AbstractMessageImpl.java:153)
 at com.sun.xml.ws.encoding.StreamSOAPCodec.encode(StreamSOAPCodec.java:108)
 at com.sun.xml.ws.encoding.SOAPBindingCodec.encode(SOAPBindingCodec.java:258)
 at com.sun.xml.ws.transport.http.HttpAdapter.encodePacket(HttpAdapter.java:326)
 at com.sun.xml.ws.transport.http.HttpAdapter.access$100(HttpAdapter.java:99)
 at com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:460)
 at com.sun.xml.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:250)
 at com.sun.xml.ws.transport.http.servlet.ServletAdapter.handle(ServletAdapter.java:140)
 at weblogic.wsee.jaxws.HttpServletAdapter$AuthorizedInvoke.run(HttpServletAdapter.java:289)
 at weblogic.wsee.jaxws.HttpServletAdapter.post(HttpServletAdapter.java:202)
 at weblogic.wsee.jaxws.JAXWSServlet.doPost(JAXWSServlet.java:237)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
 at weblogic.wsee.jaxws.JAXWSServlet.service(JAXWSServlet.java:77)
 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:3586)
 at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
 at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:121)
 at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2196)
 at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2102)
 at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1428)
 at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201)
 at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)
Caused by: javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: empdeptfacade.Dept@50dd8d -> empdeptfacade.Emp@11541d8 -> empdeptfacade.Dept@50dd8d]
 at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:282)
 at com.sun.xml.bind.v2.runtime.BridgeImpl.marshal(BridgeImpl.java:90)
 at com.sun.xml.bind.api.Bridge.marshal(Bridge.java:107)
 at com.sun.xml.ws.message.jaxb.JAXBMessage.writePayloadTo(JAXBMessage.java:317)
 ... 26 more
Caused by: com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: empdeptfacade.Dept@50dd8d -> empdeptfacade.Emp@11541d8 -> empdeptfacade.Dept@50dd8d
 at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:244)
 at com.sun.xml.bind.v2.runtime.XMLSerializer.pushObject(XMLSerializer.java:533)
 at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:627)
 at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:150)
 at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:322)
 at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:681)
 at com.sun.xml.bind.v2.runtime.property.ArrayElementNodeProperty.serializeItem(ArrayElementNodeProperty.java:65)
 at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:168)
 at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:152)
 at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:322)
 at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:681)
 at com.sun.xml.bind.v2.runtime.property.ArrayElementNodeProperty.serializeItem(ArrayElementNodeProperty.java:65)
 at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:168)
 at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:152)
 at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:322)
 at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:681)
 at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:277)
 ... 29 more

Problem here is the methods it generates are a bit too helpful. For example Dept has a method that returns all of the Employees in the Department which in turn returns the Department you are in. Now there are a variety of ways you can solve this problem but I am going for the simplest method of marking the employee accessors of Dept as transient.

You can now run the service and get a list of departments back:

Now the Emp class has a similar problem in that for each Emp you get a copy of the Dept for it. This denormlizes the data and might be a problem in certain use cases. You can consider using XMLID/XMLIDref as mentioned in the previous link but this will only work properly if you return a structure that contains the employees and the department. Otherwise you will not get any department objects back to the client. You would need to make the following changes to get this to work:


// Changes to Dept.java note XmlID has to be of type String
// otherwise JAXB will complain.

    @XmlID
    @XmlJavaTypeAdapter(LongAdapter.class)
    public Long getDeptno() {
        return deptno;
    }

// Changes to Empt.java

    @XmlIDREF
    public Dept getDept() {
        return dept;
    }

// Long Adapter

public class LongAdapter extends XmlAdapter<String, Long> {


    public Long unmarshal(String v) {
        return Long.parseLong(v);
    }

    public String marshal(Long v) {
        return v.toString();
    }
}

// Extra query method in SessionEJBBean

    public EmpDeptList queryEmpFindAllWithDepts() {
        List<Emp> list = em.createNamedQuery("Emp.findAll").getResultList();
        Set<Dept> deps = new HashSet<Dept>();
        for (Emp emp : list) {
            deps.add(emp.getDept());
        }
        
        EmpDeptList edl = new EmpDeptList();
        edl.setEmployees(list);
        edl.setDepartments(deps);
        return edl;
    }

The cool thing about JAX-B is that on the client side the object are linked back together even though in the XML message they are sent as two different lists. (Note the cast in Dept, not sure if this is a bug or a feature of using IDRef. Something to look into at a future date)

    sessionEJBBeanService = new SessionEJBBeanService();
    SessionEJBBean sessionEJBBean = sessionEJBBeanService.getSessionEJBBeanPort();
    // Add your code to call the desired methods.

    QueryEmpFindAllWithDeptsResponse allWithDepts =
        sessionEJBBean.queryEmpFindAllWithDepts(new QueryEmpFindAllWithDepts());
    Emp e = allWithDepts.getReturn().getEmployees().get(0);
    Dept d = (Dept)e.getDept();
    System.out.println(d.getDname());

Of course this exposes the limitation of the annotation programming model as it is much harder to present different XML views of your data depending with the same bean model. The key take away point I guess is that database data can be in the form or a mesh whereas you have to be careful when creating web services and generating XML that the data is mapped into a tree.

Wednesday, February 18, 2009

Updated version of JSR 235 spec : Service Data Object (SDO)

Worth a quick read if only for the ability to transfer only the change sets back over the wire. Many focused on SOA at the moment; but will be interested to see if this gets used more widely in web services.

Monday, February 16, 2009

@Resource injection with Jersey

One really annoying thing you find when playing with Jersey is that a lot of the standard annotations you are used to in JEE don't work. This is because Jersey is not entirely plumbed in to the container until it becomes a native service when JEE6 comes along.

It is possible to work around this though by creating your own InjectableProvider that is created by your own version of ServletAdapter. The code looks something like this:

package project1;

import com.sun.jersey.api.container.ContainerException;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.api.core.ResourceConfig;
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 com.sun.jersey.core.spi.component.ComponentContext;

import com.sun.jersey.core.spi.component.ComponentScope;

import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;

import java.lang.reflect.Proxy;
import java.lang.reflect.Type;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

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

import javax.naming.NamingException;

import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;

import javax.servlet.ServletConfig;

import javax.ws.rs.core.UriInfo;

import javax.xml.ws.Holder;


public class ServletAdapter
  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.Singleton;
        }

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

          final Holder value = new Holder();

          try
          {
            Context ctx = new InitialContext();

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

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

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

          return new Injectable<Object>()
          {

            public Object getValue()
            {
              return value.value;
            }
          };
        }
      });
  }
}

You then need to make the equivalent changes to your web.xml to make sure that your adapter is used instead of one of the Jersey ones.

<web-app>
    <servlet>
        <display-name>JAX-RS Servlet</display-name>
        <servlet-name>jersey</servlet-name>
        <servlet-class>project1.ServletAdapter</servlet-class>  
        <init-param>
            <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
            <param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
        </init-param>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>project1</param-value>
        </init-param>
    </servlet>
</web-app>

Now you get constructor, and field injection on classes that are directly managed by Jersey. In the bug feed application this is only going to be the top level BugFeedDB class. Jersey will inject parameters into the methods that return new resources; but the annoying thing is that @Resource cannot be added onto method parameters.

The two options here are either to pass the value injected @Resource from the root down through the resource chain through the constructors or make up your own marker annotation that can be attached anywhere. The second will probably give you tidier code.

Another quite different way to attack this problem is if you return a class rather than an instance of the class from a resource you will find that injection now works as Jersey can manage the instance. The problem is that you don't have any values from parent resources. In the bug feed case one of the values you need at the tip resource is going to be the user name captured in UserResource.

I tried a few variations and the one I settled on was to have an annotation that you could use to inject previously matched resources in the chain. So I created a marker annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface PreviousResource {
    
    
}

And then another injectable provider, note the scope here has changed from Singleton to PerRequest so that the injected value updates properly. Note that the injectable extends AbstractHttpContextInjectable so that it can get hold of HttpContext object.


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

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

        public Injectable<Object> getInjectable(ComponentContext ic,
                                                final PreviousResource r,
                                                final Type t)
        {

          return new AbstractHttpContextInjectable<Object>()
          {

            public Object getValue(HttpContext c)
            {

              List<Object> matched = c.getUriInfo().getMatchedResources();

              for (Object o: matched)
              {
                if (o.getClass() == t)
                {
                  return o;
                }
              }

              return null;
            }
          };
        }
      });

Then you can have it injected where you need it, for example here is the method to return a new resource:

@GET
@Produces("application/atom+xml")
public Feed getBugsAtom(@javax.ws.rs.core.Context
    UriInfo info, @QueryParam(value = "days")
    @DefaultValue("10")
    int days,
    @PreviousResource UserResource ur) {

  String user = ur.getUser();
  ...

}

Other options would be to write a injector that got you previously matched parameters in the URI; but that is left as an exercise to the reader. Not entirely sure how useful @PreviousResource would be in the long term; but it seemed interesting enough to write up. @Resource is definitely worth having working.

Wednesday, February 11, 2009

Mistake in security policy worked example

After some investigation it turns out that I made a mistake in the code I included in my security policy worked example that would have stopped bi-directional encryption from working. (eg if you had added Wssp1.2-2007-EncryptBody.xml to my example) I have updated the previous posting with a note and the some alternative code.

Monday, February 9, 2009

JavaDoc for Weblogic 10.3

Just a quick post, you can find the javadoc for any weblogic specific classes here.

Correlating keys in WS-SX messages with certificates you have on disk

I have been looking at a problem over the last week where we were not sure what certificate was being used in a particular case during a secure web service exchanges. It turns out that there are a number of ways a certificate could be referenced and it required a bit of maths to do the correlation. (Also in some cases the full certificate can be included but that is a rather easier correlation problem as you just need to un-base64 and diff) Both of these cases assume you have the ".der" file for the certificate to work with.

In the first example, thanks to Chris M, the SecurityTokenReference contains the serial number of the certificate:

<wsse:SecurityTokenReference
    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    wsu:Id="str_1VQOGYy2CvP18oEC">
  <X509Data
    xmlns="http://www.w3.org/2000/09/xmldsig#">
    <X509IssuerSerial>
      <X509IssuerName>CN=CertGenCAB,OU=FOR TESTING    ONLY,O=MyOrganization,L=MyTown,ST=MyState,C=US</X509IssuerName>
      <X509SerialNumber>9652974370220359889588047549019486289</X509SerialNumber>
    </X509IssuerSerial>
  </X509Data>
</wsse:SecurityTokenReference> 

Now I came up with the following which uses openssl to print out the serial number, in hex, and then convert it to decimal so you can compare it with the value in the soap message. If you know more unix voodoo than me feel free to comment with a more optimal version, thanks to Alan Davis for some help here.

[gdavison Certificates]$ ( echo ibase=16 ; openssl x509 -serial -inform der -noout  -in ClientCert.der ; echo serial ) | bc -q
9652974370220359889588047549019486289

It is worth know you can convince openssl to dump your entire certificate in a pretty form using the -text option if you want to look at it a bit closer:

[gdavison@ukp79266 Certificates]$ openssl x509 -inform der -noout -text -in ServerCert.der
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
             (Negative)79:76:ab:94:51:fe:b5:7c:a5:c8:15:88:1d:15:a6:31
        Signature Algorithm: md5WithRSAEncryption
        Issuer: C=US, ST=MyState, L=MyTown, O=MyOrganization, OU=FOR TESTING ONLY, CN=CertGenCAB
        Validity
            Not Before: Jan 19 13:46:59 2009 GMT
            Not After : Jan 20 13:46:59 2024 GMT
        Subject: C=US, ST=MyState, L=MyTown, O=MyOrganization, OU=FOR TESTING ONLY, CN=XXXXXX
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:a4:9b:ae:fe:57:0f:5b:23:11:9c:60:f0:b0:29:
                    2a:6a:2d:d5:3e:44:a9:84:c4:a3:52:7a:d2:1c:1e:
                    bf:ed:eb:5d:90:20:8f:05:da:a1:1c:7b:67:90:36:
                    2e:7d:f9:c5:6e:82:25:b8:72:9a:25:7f:20:d7:ec:
                    9b:dd:d5:a0:95:8a:a0:df:20:d7:f7:04:89:da:17:
                    9c:d7:ce:f8:7f:fa:59:03:dc:ad:3b:51:ba:ad:57:
                    f4:5a:e6:29:85:8e:02:c9:a4:36:35:fb:a2:17:70:
                    74:ab:56:a3:ed:ed:c0:d8:26:f8:68:4f:ad:78:b5:
                    53:cd:f4:7b:93:e0:05:55:fd
                Exponent: 65537 (0x10001)
    Signature Algorithm: md5WithRSAEncryption
        3f:d6:e7:0b:00:0c:14:13:90:9e:7d:de:cf:46:4d:9d:3e:f3:
        57:fa:27:6c:0c:bf:b0:9e:d8:91:93:6b:47:e5:62:31:29:52:
        b2:b5:3e:b8:1b:ac:2d:c0:b3:38:ab:e5:f6:5c:30:5f:04:7b:
        4f:90:90:75:25:81:59:33:24:e8

In the second example we have the following key info but this in case uses the sha1 of the key in base64 format:

<ns3:KeyInfo xmlns:ns3="http://www.w3.org/2000/09/xmldsig#">
  <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="str_fC2JkjjyT0mUbvXk">
    <wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#ThumbprintSHA1"      
       >eF68uMnGeydQmRc69mWanQzAek0=</wsse:KeyIdentifier>
  </wsse:SecurityTokenReference>
</ns3:KeyInfo>

You can easily use the openssl command and base64 to convert you certificate into the required format for comparison. You can work out what processes you need to apply by looking at the ValueType, ThumbprintSHA1, and the EncodingType, Base64Binary, in this case. It would be relatively easy to generate the correct string should another hashing algorithm be specified.

[gdavison Certificates]$ openssl dgst -binary -sha1 ClientCert.der | base64
eF68uMnGeydQmRc69mWanQzAek0=

The upshot of this is whilst the structure and format of the certificate files can be intimidating at first the openssl tool can be used to make things more transparent.

Friday, February 6, 2009

You know you are going to get a headache when...

You bug states that the following message in Japense is invalid...

And the output should look like this:

The only saving grace is that neither Web nor WSDL appear to be translated in Japanese so I could just about work out where my features live. For this kind of day google translator is your friend.

Thursday, February 5, 2009

What is wrong with this SOAP message?

We were dealing with an issue with database web services and came across something you don't see very often, an badly formed soap message. Before reading on take a few minutes to look at this message and try to work out what is wrong, there are actually two issues:

<?xml version = '1.0'?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <soap:Fault>
      <soap:faultcode>
        <soap:Value>soap:Sender</soap:Value>
      </soap:faultcode>
      <soap:faultstring>Error processing input</soap:faultstring>
      <soap:detail>
        ...
      </soap:detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

The most important problem is that the schema for SOAP 1.1 Envelope doesn't define "elementForDefault" so it uses the default "unqualified" form. This requires that non-top level element do not have namespace prefixes. If you examine the schema closely this will mean that Envelope, Body and Fault need an explicit namespace but faultcode et al don't. WS-I basic profile actually has a rule to catch this case. In some cases if in doubt you can run the WS-I testing tools in JDeveloper.

So after fixing the namespace the message looks like:

<?xml version = '1.0'?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <soap:Fault>
      <faultcode>
        <Value>soap:Sender</Value>
      </faultcode>
      <faultstring>Error processing input</faultstring>
      <detail>
        ...
      </detail>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

This level of confusion is probably why "qualified" is not considered by many to be a better default, for example this is how the SOAP 1.2 specification is defined. We have a audit rule in JDeveloper that makes this suggested fix when it sees it.

There is one final issue in that the faultcode element is wrong. The schema requires a QName, the Value element isn't part of any specification. It it worth noting that schema validation annotation I mentioned previously was a boon for tracking down this error.

The upshot of this is that JAX-WS would be quite happy as a client of this application just as long as it didn't throw a fault. Of course working this out was a team effort with thanks to Alan Davis who did the donkey work and original investigation and Pyounguk for providing the WS-I link.

Configuring JDeveloper to use a different install | domain of weblogic

One of the cool features in JDeveloper that only gets used now and then is the ability to configure JDeveloper to use a different install of weblogic when running internally. It could also be the case you need a custom configured Domain to support your application, for example when doing buffered web services.

To configure a server connection you need to go to Tools -> Preferences -> Run then Edit Server instances.

We are going to create a new configuration, so press the New button and you should see a screen as follows (you could of course just edit the default server configuration and this would affect the entire IDE rather than on a application by application basis):

Now by default JDeveloper assumes that you are try to connect to a remote weblogic instance. So you could just define a new connection to this server, big green plus top right, and you would be ready to go at this point. For the purposes of this blog we are going to assume that you want to let JDeveloper do the management of a server on the current machine. So we tick "Let JDeveloper manage...." and provide the location of the wls_server and where the domain in placed. You do still need to create a valid connection to where the server will start up to; but you obviously wont be able to test this server connection until after you have let JDeveloper start the instance.

The final step is to configure the listener port, in most cases you should just set this to what is configured in your domain:

So you can now assign this application server instance to a particular application in jdeveloper. First bring up the properties for the application:

Then select the custom configuration that we created before. Note you can create new configurations here also; but I showed the preferences way of doing things for completeness.

Now anything you Run/Debug/Profile/Test from inside of this application will use this server instance.

Wednesday, February 4, 2009

Where is policy configured on the weblogic console?

On common issue people have is finding the WS-Policy page on the web logic console. Once you have navigated to the web service you tend to think to click on the "Security" tab but find nothing. This is because WS-Policy, at least for JAX-RPC, can be used to configure reliability or mtom as well so the setting for these are under the configuration tab instead:

Once you get your head around the fact that policy is not just for security this kinda makes sense.

Tuesday, February 3, 2009

Let it snow....

Now those of you who have been watching the news about how badly we copy in the UK with snow, well it is time to let you into a little secret. We don't want to prepare because once every few years we get a little public holiday out of it. Kinda like christmas without the commercialism. Admittedly a lot of people I know are working from home, like myself, but you still get the feeling of something fun happening and that the entire country is sharing some great event.

So when you see the UK hunker down over a few inches of snow, remember we like it this way. (Although most climate change predictions give the UK long harsh winters when the Gulf stream eventually kicks the bucket so I suppose we should get used to it in the long term.)