/******************************************************************************* *******************************************************************************/ package at.gv.egiz.eaaf.core.impl.idp.process; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import javax.xml.stream.util.EventReaderDelegate; import javax.xml.transform.stax.StAXSource; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; import at.gv.egiz.eaaf.core.impl.idp.process.model.EndEvent; import at.gv.egiz.eaaf.core.impl.idp.process.model.ProcessDefinition; import at.gv.egiz.eaaf.core.impl.idp.process.model.ProcessNode; import at.gv.egiz.eaaf.core.impl.idp.process.model.StartEvent; import at.gv.egiz.eaaf.core.impl.idp.process.model.TaskInfo; import at.gv.egiz.eaaf.core.impl.idp.process.model.Transition; /** * Parses an XML representation of a process definition as defined by the respective XML schema. *

transitionElements = new ArrayList<>(); final List startEvents = new ArrayList<>(); reader = new EventReaderDelegate(reader) { @Override public XMLEvent nextEvent() throws XMLStreamException { XMLEvent event = super.nextEvent(); switch (event.getEventType()) { case XMLStreamConstants.START_ELEMENT: StartElement element = event.asStartElement(); QName qname = element.getName(); if (NS.equals(qname.getNamespaceURI())) { log.trace("Found process description element '{}'.", qname.getLocalPart()); Attribute id = element.getAttributeByName(new QName("id")); switch (qname.getLocalPart()) { case "ProcessDefinition": if (id != null) { pd.setId(id.getValue()); } break; case "StartEvent": StartEvent startEvent = new StartEvent(); if (id != null) { startEvent.setId(id.getValue()); } startEvents.add(startEvent); break; case "EndEvent": EndEvent endEvent = new EndEvent(); if (id != null) { endEvent.setId(id.getValue()); pd.getEndEvents().put(id.getValue(), endEvent); } break; case "Transition": transitionElements.add(element); break; case "Task": TaskInfo taskInfo = new TaskInfo(); if (id != null) { taskInfo.setId(id.getValue()); pd.getTaskInfos().put(id.getValue(), taskInfo); } Attribute async = element.getAttributeByName(new QName("async")); if (async != null) { taskInfo.setAsync(Boolean.valueOf(async.getValue())); } Attribute implementingClass = element.getAttributeByName(new QName("class")); if (implementingClass != null) { taskInfo.setTaskImplementingClass(implementingClass.getValue()); } break; } } break; } return event; } }; // validator is not thread-safe Validator validator = LazyProcessDefinitionSchemaHolder.PD_SCHEMA_INSTANCE.newValidator(); validator.validate(new StAXSource(reader)); log.trace("Process definition successfully schema validated."); // perform some basic checks log.trace("Building model and performing some plausibility checks."); if (startEvents.size() != 1) { throw new ProcessDefinitionParserException("A ProcessDefinition must contain exactly one single StartEvent."); } pd.setStartEvent(startEvents.get(0)); // link transitions Iterator transitions = transitionElements.iterator(); while (transitions.hasNext()) { StartElement element = transitions.next(); Transition transition = new Transition(); Attribute id = element.getAttributeByName(new QName("id")); if (id != null) { transition.setId(id.getValue()); } Attribute conditionExpression = element.getAttributeByName(new QName("conditionExpression")); if (conditionExpression != null) { transition.setConditionExpression(conditionExpression.getValue()); } Attribute from = element.getAttributeByName(new QName("from")); if (from != null) { ProcessNode fromNode = pd.getProcessNode(from.getValue()); if (fromNode == null) { throw new ProcessDefinitionParserException("Transition's 'from'-attribute refers to a non-existing event or task '" + from.getValue() + '.'); } if (fromNode instanceof EndEvent) { throw new ProcessDefinitionParserException("Transition cannot start from end event."); } transition.setFrom(fromNode); fromNode.getOutgoingTransitions().add(transition); } Attribute to = element.getAttributeByName(new QName("to")); if (to != null) { ProcessNode toNode = pd.getProcessNode(to.getValue()); if (toNode == null) { throw new ProcessDefinitionParserException("Transition's 'to'-attribute refers to a non-existing event or task '" + to.getValue() + '.'); } transition.setTo(toNode); toNode.getIncomingTransitions().add(transition); } if (transition.getConditionExpression() == null && Objects.equals(transition.getFrom(), transition.getTo())) { throw new ProcessDefinitionParserException("Transition's 'from' equals its 'to'. Since no 'conditionExpression' has been set this will cause a loop."); } } log.debug("Process definition '{}' successfully parsed.", pd.getId()); return pd; } catch (ProcessDefinitionParserException e) { throw e; } catch (XMLStreamException|IOException e) { throw new ProcessDefinitionParserException("Unable to read process definition from inputstream.", e); } catch (SAXException e) { throw new ProcessDefinitionParserException("Schema validation of process description failed.", e); } catch (Exception e) { throw new ProcessDefinitionParserException("Internal error creating process definition from inputstream.", e); } finally { if (reader != null) { try { reader.close(); } catch (XMLStreamException e) { // error freeing resources } } } } }