/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: AbstractProcessDefinitionDirectory.java 2742 2008-04-07 22:37:22Z mlipp $
*/
package de.danet.an.workflow.domain;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.jaxen.JaxenException;
import org.jaxen.XPath;
import org.jaxen.jdom.JDOMXPath;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.input.SAXHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLFilter;
import de.danet.an.util.sax.XmlnsUrisPatcher;
import de.danet.an.workflow.api.ImportException;
import de.danet.an.workflow.api.PrioritizedMessage;
import de.danet.an.workflow.api.ProcessDefinition;
import de.danet.an.workflow.localapi.ProcessDefinitionDirectoryLocal;
import de.danet.an.workflow.util.CollectingErrorHandler;
import de.danet.an.workflow.util.XPDLUtil;
/**
* This class provides a basic implementation of
* {@link de.danet.an.workflow.localapi.ProcessDefinitionDirectoryLocal
* <code>ProcessDefinitionDirectory</code>}.
*/
public abstract class AbstractProcessDefinitionDirectory
implements ProcessDefinitionDirectoryLocal {
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog
(AbstractProcessDefinitionDirectory.class);
/**
* Parses the input that describe process definitions and converts it
* into a list of {@link ProcessDefinition <code>ProcessDefinition</code>}.
*
* @param toParse the input that describes the process definition as XPDL.
* @return a list; the first element of this list is a list of prioritized
* messages {@link de.danet.an.workflow.api.PrioritizedMessage
* <code>PrioritizedMessage</code>} and then follows all the parsed process
* definitions.
* @exception ImportException if the XPDL is not correct.
*/
public static List parseProcessDefinitions(InputSource toParse)
throws ImportException {
List resultList = new ArrayList();
List processDefs = new ArrayList();
CollectingErrorHandler eh = new CollectingErrorHandler();
Document doc = null;
try {
SAXParserFactory spf = SAXParserFactory.newInstance ();
spf.setValidating (true);
spf.setNamespaceAware (true);
maybeSetFeature
(spf, "http://xml.org/sax/features/namespace-prefixes", eh);
SAXParser sp = spf.newSAXParser();
maybeSetProperty
(sp, "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema", eh);
maybeSetProperty
(sp, "http://java.sun.com/xml/jaxp/properties/schemaSource",
schemaUris(), eh);
SAXHandler sh = new SAXHandler ();
XMLFilter filter = new XmlnsUrisPatcher ();
filter.setParent(sp.getXMLReader());
filter.setContentHandler(sh);
filter.setDTDHandler(sh);
filter.setEntityResolver(sh);
filter.setErrorHandler(eh);
// build Document
filter.parse(toParse);
doc = sh.getDocument();
Element root = doc.getRootElement();
if (root == null || !root.getName().equals ("Package")
|| !root.getNamespaceURI().equals (XPDLUtil.XPDL_NS)) {
eh.add (new PrioritizedMessage
(PrioritizedMessage.Priority.ERROR,
"ImportMessages#package.missing"));
} else if (eh.getErrors().size() == 0
&& eh.getFatalErrors().size() == 0) {
ProcDefValidator procDefValidator = new ProcDefValidator();
procDefValidator.validate(doc, eh);
processDefs = AbstractProcessDefinitionDirectory
.getProcessDefinitionsFromXPDL(doc);
}
} catch (JDOMException e) {
eh.add (new PrioritizedMessage (PrioritizedMessage.Priority.ERROR,
e.getMessage()));
} catch (IOException e) {
eh.add (new PrioritizedMessage (PrioritizedMessage.Priority.ERROR,
e.getMessage()));
} catch (IllegalArgumentException e) {
eh.add (new PrioritizedMessage (PrioritizedMessage.Priority.ERROR,
e.getMessage()));
} catch (JaxenException e) {
DefaultProcessDefinition.logger.error (e.getMessage(), e);
throw new IllegalArgumentException
("Cannot traverse XPDL-JDOM: " + e.getMessage());
} catch (SAXException e) {
eh.add (new PrioritizedMessage (PrioritizedMessage.Priority.ERROR,
e.getMessage()));
} catch (ParserConfigurationException e) {
eh.add (new PrioritizedMessage (PrioritizedMessage.Priority.ERROR,
e.getMessage()));
}
List pms = eh.getMessages();
if (eh.getErrors().size() > 0 || eh.getFatalErrors().size() > 0) {
throw new ImportException
("There have been problems while parsing XPDL", pms);
}
// the first element of the result list is list of prioritized messages
resultList.add(pms);
// follows the list of process definitions
resultList.addAll(processDefs);
return resultList;
}
private static void maybeSetFeature
(SAXParserFactory spf, String feature, CollectingErrorHandler eh)
throws ParserConfigurationException {
try {
spf.setFeature (feature, true);
} catch (SAXNotRecognizedException e) {
logger.warn
("Feature " + feature + " not recognized, result may be "
+ "inacurate: " + e.getMessage(), e);
eh.add (new PrioritizedMessage (PrioritizedMessage.Priority.WARN,
e.getMessage()));
} catch (SAXNotSupportedException e) {
logger.warn
("Feature " + feature + " not recognized, result may be " +
"inacurate: " + e.getMessage(), e);
eh.add (new PrioritizedMessage (PrioritizedMessage.Priority.WARN,
e.getMessage()));
}
}
private static void maybeSetProperty
(SAXParser sp, String property, Object value,
CollectingErrorHandler eh) {
try {
sp.setProperty (property, value);
} catch (SAXNotRecognizedException e) {
logger.warn
("Property " + property + " not recognized, result may "
+ "be inacurate: " + e.getMessage(), e);
} catch (SAXNotSupportedException e) {
logger.warn
("Property " + property + " not recognized, result may "
+ "be inacurate: " + e.getMessage(), e);
}
}
private static Object[] schemaUris() throws IllegalStateException {
try {
List schemas = new ArrayList();
Set schemaSids = new HashSet();
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Enumeration rese = cl.getResources
("de/danet/an/workflow/resources/schema.locations");
// there are buggy classloaders that do not implement
// getResources properly. As a workaround, we make an
// additional call to getResource if getResources returns
// an empty result.
if (!rese.hasMoreElements()) {
Object r = cl.getResource
("de/danet/an/workflow/resources/schema.locations");
if (r != null) {
Vector v = new Vector(1);
v.add (r);
rese = v.elements();
}
}
while (rese.hasMoreElements()) {
URL res = (URL)rese.nextElement();
BufferedReader locFile = new BufferedReader
(new InputStreamReader(res.openStream(), "ISO-8859-1"));
for (String line = locFile.readLine(); line != null;
line = locFile.readLine()) {
if (line.indexOf('#') >= 0) {
line = line.substring(0, line.indexOf('#'));
}
line = line.trim();
if (line.length() == 0) {
continue;
}
int equalsPos = line.indexOf("=");
if (equalsPos < 0) {
continue;
}
String sid = line.substring(0, equalsPos).trim();
String sloc = line.substring(equalsPos + 1).trim();
URL schemaUrl = new URL (res, sloc);
try {
if (logger.isDebugEnabled()) {
InputStream is = schemaUrl.openStream();
is.close();
}
if (!schemaSids.contains(sid)) {
schemas.add(schemaUrl.toString());
schemaSids.add(sid);
}
} catch (IOException e) {
logger.debug("Cannot open " + schemaUrl.toString()
+ " for " + sid + ": " + e.getMessage(), e);
}
}
}
return schemas.toArray();
} catch (IOException ioe) {
throw new IllegalStateException ();
}
}
private static List getProcessDefinitionsFromXPDL(Document doc)
throws JDOMException, JaxenException, ImportException {
List resultList = new ArrayList();
List processes = new ArrayList();
Element root = doc.getRootElement();
// Namespace
Namespace namespace = Namespace.getNamespace (XPDLUtil.XPDL_NS);
// cut out the WorkflowProcess nodes
XPath path = new JDOMXPath
("/xpdl:Package/xpdl:WorkflowProcesses/xpdl:WorkflowProcess");
path.addNamespace("xpdl", XPDLUtil.XPDL_NS);
Iterator processListIterator = path.selectNodes(doc).iterator();
while (processListIterator.hasNext()) {
Element child = (Element)processListIterator.next();
processes.add(child);
}
if (processes.size() == 0) {
return resultList;
}
Element parent = root.getChild("WorkflowProcesses", namespace);
parent.removeContent();
Iterator it2 = processes.iterator();
while (it2.hasNext()) {
Element rootClone = (Element)root.clone();
Element newProcess = (Element)it2.next();
// new Document
// import the package description and add to new document
Document newDoc = new Document(rootClone);
// import process definition and add under processes
Element newProcesses
= rootClone.getChild("WorkflowProcesses", namespace);
newProcesses.addContent(newProcess);
ProcessDefinition pd = new DefaultProcessDefinition(newDoc);
// add to resultlist
resultList.add(pd);
}
return resultList;
}
}