Wednesday, June 15, 2011

java.io.FileNotFoundException: http://www.opensymphony.com/osworkflow/workflow_2_8.dtd

Have you started seeing the following error recently in your application that uses open symphony's osworkflow-2.8.0.jar?
Caused by: java.io.FileNotFoundException: http://www.opensymphony.com/osworkflow/workflow_2_8.dtd
 at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
 at org.apache.xerces.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
 at org.apache.xerces.impl.XMLEntityManager.startEntity(Unknown Source)
 at org.apache.xerces.impl.XMLEntityManager.startDTDEntity(Unknown Source)
 at org.apache.xerces.impl.XMLDTDScannerImpl.setInputSource(Unknown Source)
 at org.apache.xerces.impl.XMLDocumentScannerImpl$DTDDispatcher.dispatch(Unknown Source)
 at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
 at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
 at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
 at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
 at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
 at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
 at javax.xml.parsers.DocumentBuilder.parse(Unknown Source)
Reason for the above error is clear from http://www.opensymphony.com
Looking for an OpenSymphony project? Unfortunately, OpenSymphony has seen it's final days.
All the URLs to www.opensymphony.com including the URL for the DTD is now pointed to the home page. So the XML parser cannot retrieve the DTD to validate the workflow xml file. If you have a production app that uses osworkflow jar, what do you do to get the app working again?

There are couple of options:

1. You can copy the DTD bundled inside osworkflow-2.8.0.jar to your website with a static IP address under /osworkflow path, edit /etc/hosts (or C:\Windows\system32\drivers\etc\host in windows) to point www.osworkflow.com to your IP address. This will make your app to find the DTD on your server.

2. The right way to fix the issue is in your code. The place where you parse the workflow XML file.

for example:
// Load the workflow xml
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
File file = new File(workflowXmlFilename);
Document docWorkflow = builder.parse(file);
where workflowXmlFilename is your workflow xml file with the DTD pointing to http://www.opensymphony.com/osworkflow/workflow_2_8.dtd.

To resolve the DTD inside the osworkflow-2.8.0.jar, you need to set the EntityResolver of DocumentBuilder to the one provided by open symphony. Otherwise XML parser will use the default resolving mechanism.
// Load the workflow xml
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(new DTDEntityResolver());
File file = new File(workflowXmlFilename);
Document docWorkflow = builder.parse(file);
The above code
builder.setEntityResolver(new DTDEntityResolver());
tells the parser to use com.opensymphony.workflow.loader.DTDEntityResolver that has the special logic to reference the DTD inside the jar. Here is the relevant part of the DTDEntityResolver.java
URL url = new URL(systemId);
String file = url.getFile();

if ((file != null) && (file.indexOf('/') > -1)) {
  file = file.substring(file.lastIndexOf('/') + 1);
}
if ("www.opensymphony.com".equals(url.getHost()) && systemId.endsWith(".dtd")) {
  InputStream is = getClass().getResourceAsStream("/META-INF/" + file);

  if (is == null) {
    is = getClass().getResourceAsStream('/' + file);
  }

  if (is != null) {
    return new InputSource(is);
  }
}
You can get the source code of osworkflow-2.8.0.jar from https://maven.atlassian.com/content/repositories/atlassian-3rdparty/opensymphony/osworkflow/2.8.0/osworkflow-2.8.0-sources.jar.

Long live Open Source !!

Wednesday, May 18, 2011

PL/SQL - REST Call and XML Parsing

Looking for an example to make a REST API call in PL/SQL and parse the xml response from the call? Here is one way to do it.
DECLARE
 req     utl_http.req;
 resp    utl_http.resp;
 line    VARCHAR2(1024);
 url     VARCHAR2(1024);
 content VARCHAR2(1024);

 p       xmlparser.Parser;
 doc     xmldom.DOMDocument;
 element    xmldom.DOMElement;
 nodeList xmldom.DOMNodeList;
 node     xmldom.DOMNode;
BEGIN
 dbms_output.enable(1000000);

 url := utl_url.escape('http://host:port/app/rest/something');

 -- example xml response:
 --<?xml version="1.0" encoding="UTF-8"?><something><name>Joe</name><age>40</age></something>

 req := utl_http.begin_request(url);
 utl_http.set_header(req, 'User-Agent', 'Mozilla/4.0');
 resp := utl_http.get_response(req);
 content := '';
 BEGIN
     LOOP
       utl_http.read_line(resp, line, TRUE);
       content := content || line;
     END LOOP;
     utl_http.end_response(resp);
     EXCEPTION
         WHEN utl_http.end_of_body THEN
           utl_http.end_response(resp);
 END;

 dbms_output.put_line(content);

 p := xmlparser.newParser;
 xmlparser.setValidationMode(p, FALSE);
 xmlparser.parseBuffer(p, content);
 doc  := xmlparser.getDocument(p);

 element := xmldom.getDocumentElement(doc);

 nodeList := xmldom.getElementsByTagName(element, 'name');
 node := xmldom.item(nodeList, 0);
 node := xmldom.getFirstChild(node);
 dbms_output.put_line('name: ' || xmldom.getNodeValue(node));

 nodeList := xmldom.getElementsByTagName(element, 'age');
 node := xmldom.item(nodeList, 0);
 node := xmldom.getFirstChild(node);
 dbms_output.put_line('age: ' || xmldom.getNodeValue(node));
END;

Monday, May 16, 2011

Ever stuck in eclipse splash screen?

There have been times my Spring Source Tool Suite (based on eclipse) won't start after a crash or machine restart after automatic windows update when IDE was running. It mainly get stuck in the splash screen after selecting the workspace. One way to get the IDE started is to remove .snap file from
.metadata\.plugins\org.eclipse.core.resources
under your workspace folder and start IDE again.