From 759ac5f42c6aff901dbeede4fbf1a1d2e08cad0f Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Wed, 4 Dec 2019 19:43:32 +0100 Subject: common EGIZ code-style refactoring --- .../impl/idp/process/ProcessDefinitionParser.java | 430 +++++++++++---------- 1 file changed, 222 insertions(+), 208 deletions(-) (limited to 'eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessDefinitionParser.java') diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessDefinitionParser.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessDefinitionParser.java index f817f9fb..63ae66d5 100644 --- a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessDefinitionParser.java +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/process/ProcessDefinitionParser.java @@ -1,29 +1,22 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European + * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in + * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - * - * This product combines work with different licenses. See the "NOTICE" text - * file for details on the various modules and licenses. - * The "NOTICE" text file is part of the distribution. Any derivative works - * that you distribute must include a readable copy of the "NOTICE" text file. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * Unless required by applicable law or agreed to in writing, software distributed under the Licence + * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the Licence for the specific language governing permissions and limitations under + * the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text file for details on the + * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative + * works that you distribute must include a readable copy of the "NOTICE" text file. +*/ + package at.gv.egiz.eaaf.core.impl.idp.process; import java.io.IOException; @@ -32,7 +25,6 @@ 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; @@ -48,203 +40,225 @@ 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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; /** * 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 - } - } - } - } + + private static final String NS = + "http://reference.e-government.gv.at/namespace/moa/process/definition/v1"; + + private static Logger log = LoggerFactory.getLogger(ProcessDefinitionParser.class); + + private static class LazyProcessDefinitionSchemaHolder { + private static final Schema PD_SCHEMA_INSTANCE; + + static { + try (InputStream in = + ProcessDefinitionParser.class.getResourceAsStream("/process/ProcessDefinition.xsd")) { + log.trace("Compiling process definition schema."); + final SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + // schema is thread-safe + PD_SCHEMA_INSTANCE = factory.newSchema(new StreamSource(in)); + } catch (final Exception e) { + throw new RuntimeException("Unable to compile process definition schema.", e); + } + } + } + + /** + * Parses an XML representation of a process definition. The representation is being validated in + * order to suffice the related XML schema. + * + * @param processDefinitionInputStream The process definition. + * @return A new process definition. + * @throws ProcessDefinitionParserException Thrown in case of error parsing the process + * definition. + */ + public ProcessDefinition parse(final InputStream processDefinitionInputStream) + throws ProcessDefinitionParserException { + XMLEventReader reader = null; + final ProcessDefinition pd = new ProcessDefinition(); + log.debug("Parsing and validating process definition."); + try { + + // Standard implementation of XMLInputFactory seems not to be thread-safe + final XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + reader = inputFactory.createXMLEventReader(processDefinitionInputStream); + + final List transitionElements = new ArrayList<>(); + final List startEvents = new ArrayList<>(); + + reader = new EventReaderDelegate(reader) { + + @Override + public XMLEvent nextEvent() throws XMLStreamException { + final XMLEvent event = super.nextEvent(); + + switch (event.getEventType()) { + case XMLStreamConstants.START_ELEMENT: + final StartElement element = event.asStartElement(); + final QName qname = element.getName(); + + if (NS.equals(qname.getNamespaceURI())) { + log.trace("Found process description element '{}'.", qname.getLocalPart()); + final Attribute id = element.getAttributeByName(new QName("id")); + + switch (qname.getLocalPart()) { + case "ProcessDefinition": + if (id != null) { + pd.setId(id.getValue()); + } + break; + case "StartEvent": + final StartEvent startEvent = new StartEvent(); + if (id != null) { + startEvent.setId(id.getValue()); + } + startEvents.add(startEvent); + break; + case "EndEvent": + final 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": + final TaskInfo taskInfo = new TaskInfo(); + if (id != null) { + taskInfo.setId(id.getValue()); + pd.getTaskInfos().put(id.getValue(), taskInfo); + } + final Attribute async = element.getAttributeByName(new QName("async")); + if (async != null) { + taskInfo.setAsync(Boolean.valueOf(async.getValue())); + } + final Attribute implementingClass = + element.getAttributeByName(new QName("class")); + if (implementingClass != null) { + taskInfo.setTaskImplementingClass(implementingClass.getValue()); + } + break; + default: + log.warn("Ignore unknown event: {}", qname); + break; + } + + } + + break; + default: + log.warn("Ignore unknown event: {}", event); + break; + } + + return event; + } + + }; + + // validator is not thread-safe + final 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 + final Iterator transitions = transitionElements.iterator(); + while (transitions.hasNext()) { + final StartElement element = transitions.next(); + final Transition transition = new Transition(); + final Attribute id = element.getAttributeByName(new QName("id")); + if (id != null) { + transition.setId(id.getValue()); + } + final Attribute conditionExpression = + element.getAttributeByName(new QName("conditionExpression")); + if (conditionExpression != null) { + transition.setConditionExpression(conditionExpression.getValue()); + } + final Attribute from = element.getAttributeByName(new QName("from")); + if (from != null) { + final 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); + } + final Attribute to = element.getAttributeByName(new QName("to")); + if (to != null) { + final 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 (final ProcessDefinitionParserException e) { + throw e; + } catch (XMLStreamException | IOException e) { + throw new ProcessDefinitionParserException( + "Unable to read process definition from inputstream.", e); + } catch (final SAXException e) { + throw new ProcessDefinitionParserException("Schema validation of process description failed.", + e); + } catch (final Exception e) { + throw new ProcessDefinitionParserException( + "Internal error creating process definition from inputstream.", e); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (final XMLStreamException e) { + e.printStackTrace(); + + } + } + } + } } -- cgit v1.2.3