Wednesday, May 25, 2011

Jersey issue when running with ADF / Oracle XDK

We have discovered a late breaking bug in WLS that prevents Jersey applications from de-serializing XML and in some cases JSON documents. (So GET will work but PUT or POST will fail) This currently affects JDeveloper 11.1.1.5.0 aka PS4. You might see an exception that looks like this:

javax.xml.parsers.FactoryConfigurationError: WebLogicSAXParser
cannot be created.SAX feature
'
http://xml.org/sax/features/external-general-entities'
not
supported.
at weblogic.xml.jaxp.RegistrySAXParser.<init>(RegistrySAXParser.java:73)
at weblogic.xml.jaxp.RegistrySAXParser.<init>(RegistrySAXParser.java:46)
at
weblogic.xml.jaxp.RegistrySAXParserFactory.newSAXParser(RegistrySAXParserFacto
ry.java:91) 
...

When you create ADF components in an application you will find that at the .EAR level a weblogic-application.xml is created at deployment time which override the default WLS xml parser version to use the Oracle XDK version. You might see something like this:

<parser-factory>
<saxparser-factory>oracle.xml.jaxp.JXSAXParserFactory</saxparser-factory>
<document-builder-factory>oracle.xml.jaxp.JXDocumentBuilderFactory</document-builder-factory>
<transformer-factory>oracle.xml.jaxp.JXSAXTransformerFactory</transformer-factory>
</parser-factory>

When Jersey tries to convert a document to JAX-B classes it sensible sets a feature on the XML parser that prevents resolving external entities to prevent certain security attacks. This code in SAXParserContentProvider does understand that some parser don't support this feature as you can see:

@Override
    protected SAXParserFactory getInstance() {
        SAXParserFactory f = SAXParserFactory.newInstance();

        f.setNamespaceAware(true);
        
        if (!disableXmlSecurity) {
            try {
                f.setFeature("http://xml.org/sax/features/external-general-entities", Boolean.FALSE);
            } catch (Exception ex) {
                throw new RuntimeException("Security features for the SAX parser could not be enabled",  ex);
            }

            try {
                f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
            } catch (Exception ex) {
                LOGGER.log(Level.WARNING, 
                        "JAXP feature XMLConstants.FEATURE_SECURE_PROCESSING cannot be set on a SAXParserFactory. " +
                        "External general entity processing is disbaled but other potential securty related" +
                        " features will not be enabled.", 
                        ex);
            }
        }

        return f;
    }

This would normally be the end of it if it was not for bug 12543845, where the invalid features is incorrectly stored prevent the future creation of any parsers.

Luckily there is a workaround in Jersey, you can simply set this using a Servlet init param as follows:

<servlet>
    <servlet-name>jersey</servlet-name>
    <servlet-class>com.sun.jersey.
spi.container.servlet.ServletContainer</servlet-class>
   <init-param>

<param-name>com.sun.jersey.config.feature.DisableXmlSecurity</param-name>
      <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet> 

Be careful not to apply this property when you are not using the XDK parser otherwise you might open yourself up to a range of attacks which is not going to be a good thing.

Updated 30th March 2012: You might also need a similar workaround for the Jersey client as shown here. (I have raised this Jersey issue to see if this can be improved)

DefaultClientConfig cc = new DefaultClientConfig();
  cc.getFeatures().put(
    FeaturesAndProperties.FEATURE_DISABLE_XML_SECURITY, true);
  Client c = Client.create(cc);

Updated 15th August 2014: Slightly different for JAX-RS 2.0 client under Jersey, set this property on a configurable such as your client instance:

   configurable.property(MessageProperties.XML_SECURITY_DISABLE, Boolean.TRUE);


Tuesday, May 10, 2011

JDeveloper, the Endorsed Directory, bootclasspath, and JAX-WS 2.2.x

I have been working with JAX-WS 2.2.x recently and one of problem you might fact whilst playing with a client is that it uses more recent version of the JAX-WS API than that supplied with JDK 1.6. This shouldn't be a problem normally but there are some issues that might get in your way when using JDeveloper.

The first thing to note is that I am going to ignore the standard advice to user the java.endorsed.dirs system property as it can only specify a directory and not individual jar files. This is not always a problem; but if your jars are not packaged in a convenient way it means you have to move then to a new directory. Instead I am going for the slightly less standard -Xbootclasspath/p: property which prepends the jars to the boot classpath with much the same effect and I can specify the paths to individual jars. I will note with the screen grabs where you might go differently if you want to ignore this advice.

First things first you need to configure the compiler otherwise when you try to compile a web services client, in this case, you find error messages relating to the constructors on the Service class. You can find these settings under project properties. You could use the -endorsedpath here if you wish. For my purposes I had to place both the jax-xml-bind.jar and jax-xml-ws.jar on the bootclasspath. Of course use you platform specific path separator between the two paths. (If you are doing JAX-WS you are going to need the matching version of JAX-B)

Unfortunately you might find that this point that the project will fail to compile due to bug 12538161, there is a workaround which is to ask the compilation to happen as a separate process. This is going to be slightly slower; but in most cases you are not going to notice the difference.

Depending on your deployment context you might also like to update the run options for the your project, again I have used the bootclasspath but you could use the -Djava.endorsed.dirs depending on how you dependencies are laid out.

You can compile and run against the new API which will solve 99% of problem in the area. Fortunately only a few Java API's are packaged in this way. Hopefully in JDK 8 we can get the modularization sorted out so this sort of thing will be less of a problem.