summaryrefslogtreecommitdiffstats
path: root/bpmn/MSOCoreBPMN/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'bpmn/MSOCoreBPMN/src/main/java')
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BPMNLogger.java33
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BadInjectedFieldException.java52
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BaseTask.java529
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/HealthCheckHandler.java203
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/LogTask.java98
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/MissingInjectedFieldException.java39
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/PropertyConfiguration.java440
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ReadConfigTask.java111
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ReadFileTask.java117
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ResponseBuilder.java297
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/RollbackData.java108
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/URNMappingsTask.java32
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/WorkflowException.java84
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/XQueryScriptTask.java243
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/json/JsonUtils.java443
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/mybatis/CustomMyBatisSessionFactory.java102
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/mybatis/URNMapping.java122
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/plugins/LoggingAndURNMappingPlugin.java424
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/plugins/WorkflowExceptionPlugin.java170
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/xml/XmlTool.java340
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/test/CamundaDBSetup.java108
-rw-r--r--bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/test/PropertyConfigurationSetup.java315
22 files changed, 4410 insertions, 0 deletions
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BPMNLogger.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BPMNLogger.java
new file mode 100644
index 0000000000..821380f562
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BPMNLogger.java
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import org.openecomp.mso.logger.MsoLogger;
+
+public class BPMNLogger {
+ private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+
+ public static void debug (String isDebugLogEnabled, String LogText) {
+ if (("true").equalsIgnoreCase(isDebugLogEnabled))
+ msoLogger.debug(LogText);
+ }
+}
+
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BadInjectedFieldException.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BadInjectedFieldException.java
new file mode 100644
index 0000000000..84c0954386
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BadInjectedFieldException.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+public class BadInjectedFieldException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * @param fieldName the field name
+ * @param taskName the task name
+ * @param info additional information, e.g. the field value
+ */
+ public BadInjectedFieldException(String fieldName, String taskName,
+ Object info) {
+ super(taskName + " injected field '" + fieldName + "' is bad: " + info);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param fieldName the field name
+ * @param taskName the task name
+ * @param info additional information, e.g. the field value
+ * @param cause the cause
+ */
+ public BadInjectedFieldException(String fieldName,
+ String taskName, Object info, Throwable cause) {
+ super(taskName + " injected field '" + fieldName + "' is bad: "
+ + info, cause);
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BaseTask.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BaseTask.java
new file mode 100644
index 0000000000..849c8ba4f8
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/BaseTask.java
@@ -0,0 +1,529 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+import org.camunda.bpm.engine.delegate.Expression;
+import org.camunda.bpm.engine.delegate.JavaDelegate;
+
+/**
+ * Base class for service tasks.
+ */
+public abstract class BaseTask implements JavaDelegate {
+
+ /**
+ * Get the value of a required field. This method throws
+ * MissingInjectedFieldException if the expression is null, and
+ * BadInjectedFieldException if the expression evaluates to a null
+ * value.
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the field value
+ */
+ protected Object getField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ return getFieldImpl(expression, execution, fieldName, false);
+ }
+
+ /**
+ * Gets the value of an optional field. There are three conditions
+ * in which this method returns null:
+ * <p>
+ * <ol>
+ * <li> The expression itself is null (i.e. the field is missing
+ * altogether.</li>
+ * <li>The expression evaluates to a null value.</li>
+ * <li>The expression references a single variable which has not
+ * been set.</li>
+ * </ol>
+ * <p>
+ * Examples:<br>
+ * Expression ${x} when x is null: return null<br>
+ * Expression ${x} when x is unset: return null<br>
+ * Expression ${x+y} when x and/or y are unset: exception<br>
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the field value, possibly null
+ */
+ protected Object getOptionalField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ return getFieldImpl(expression, execution, fieldName, true);
+ }
+
+ /**
+ * Get the value of a required output variable field. This method
+ * throws MissingInjectedFieldException if the expression is null, and
+ * BadInjectedFieldException if the expression produces a null or
+ * illegal variable name. Legal variable names contain only letters,
+ * numbers, and the underscore character ('_').
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the output variable name
+ */
+ protected String getOutputField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ Object o = getFieldImpl(expression, execution, fieldName, false);
+ if (o instanceof String) {
+ String variable = (String) o;
+ if (!isLegalVariable(variable)) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "'" + variable
+ + "' is not a legal variable name");
+ }
+ return variable;
+ } else {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "expected a variable name string"
+ + ", got object of type " + o.getClass().getName());
+ }
+ }
+
+ /**
+ * Get the value of an optional output variable field. This method
+ * throws BadInjectedFieldException if the expression produces an illegal
+ * variable name. Legal variable names contain only letters, numbers,
+ * and the underscore character ('_').
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the output variable name, possibly null
+ */
+ protected String getOptionalOutputField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ Object o = getFieldImpl(expression, execution, fieldName, true);
+ if (o instanceof String) {
+ String variable = (String) o;
+ if (!isLegalVariable(variable)) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "'" + variable
+ + "' is not a legal variable name");
+ }
+ return variable;
+ } else if (o == null) {
+ return null;
+ } else {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "expected a variable name string"
+ + ", got object of type " + o.getClass().getName());
+ }
+ }
+
+ /**
+ * Get the value of a required string field. This method throws
+ * MissingInjectedFieldException if the expression is null, and
+ * BadInjectedFieldException if the expression evaluates to a null
+ * value.
+ * <p>
+ * Note: the result is coerced to a string value, if necessary.
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the field value
+ */
+ protected String getStringField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ Object o = getFieldImpl(expression, execution, fieldName, false);
+ if (o instanceof String) {
+ return (String) o;
+ } else {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "cannot convert '" + o.toString()
+ + "' to Integer");
+ }
+ }
+
+ /**
+ * Gets the value of an optional string field. There are three conditions
+ * in which this method returns null:
+ * <p>
+ * <ol>
+ * <li> The expression itself is null (i.e. the field is missing
+ * altogether.</li>
+ * <li>The expression evaluates to a null value.</li>
+ * <li>The expression references a single variable which has not
+ * been set.</li>
+ * </ol>
+ * <p>
+ * Examples:<br>
+ * Expression ${x} when x is null: return null<br>
+ * Expression ${x} when x is unset: return null<br>
+ * Expression ${x+y} when x and/or y are unset: exception<br>
+ * <p>
+ * Note: the result is coerced to a string value, if necessary.
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the field value, possibly null
+ */
+ protected String getOptionalStringField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ Object o = getFieldImpl(expression, execution, fieldName, true);
+ if (o instanceof String) {
+ return (String) o;
+ } else if (o == null) {
+ return null;
+ } else {
+ return o.toString();
+ }
+ }
+
+ /**
+ * Get the value of a required integer field. This method throws
+ * MissingInjectedFieldException if the expression is null, and
+ * BadInjectedFieldException if the expression evaluates to a null
+ * value or a value that cannot be coerced to an integer.
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the field value
+ */
+ protected Integer getIntegerField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ Object o = getFieldImpl(expression, execution, fieldName, false);
+ if (o instanceof Integer) {
+ return (Integer) o;
+ } else {
+ try {
+ return Integer.parseInt(o.toString());
+ } catch (NumberFormatException e) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "cannot convert '" + o.toString()
+ + "' to Integer");
+ }
+ }
+ }
+
+ /**
+ * Gets the value of an optional integer field. There are three conditions
+ * in which this method returns null:
+ * <p>
+ * <ol>
+ * <li> The expression itself is null (i.e. the field is missing
+ * altogether.</li>
+ * <li>The expression evaluates to a null value.</li>
+ * <li>The expression references a single variable which has not
+ * been set.</li>
+ * </ol>
+ * <p>
+ * Examples:<br>
+ * Expression ${x} when x is null: return null<br>
+ * Expression ${x} when x is unset: return null<br>
+ * Expression ${x+y} when x and/or y are unset: exception<br>
+ * <p>
+ * Note: the result is coerced to an integer value, if necessary. This
+ * method throws BadInjectedFieldException if the result cannot be coerced
+ * to an integer.
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the field value, possibly null
+ */
+ protected Integer getOptionalIntegerField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ Object o = getFieldImpl(expression, execution, fieldName, true);
+ if (o instanceof Integer) {
+ return (Integer) o;
+ } else if (o == null) {
+ return null;
+ } else {
+ try {
+ return Integer.parseInt(o.toString());
+ } catch (NumberFormatException e) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "cannot convert '" + o.toString()
+ + "' to Integer");
+ }
+ }
+ }
+
+ /**
+ * Gets the value of an optional long field. There are three conditions
+ * in which this method returns null:
+ * <p>
+ * <ol>
+ * <li> The expression itself is null (i.e. the field is missing
+ * altogether.</li>
+ * <li>The expression evaluates to a null value.</li>
+ * <li>The expression references a single variable which has not
+ * been set.</li>
+ * </ol>
+ * <p>
+ * Examples:<br>
+ * Expression ${x} when x is null: return null<br>
+ * Expression ${x} when x is unset: return null<br>
+ * Expression ${x+y} when x and/or y are unset: exception<br>
+ * <p>
+ * Note: the result is coerced to a long value, if necessary. This
+ * method throws BadInjectedFieldException if the result cannot be coerced
+ * to a long.
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the field value, possibly null
+ */
+ protected Long getOptionalLongField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ Object o = getFieldImpl(expression, execution, fieldName, true);
+ if (o instanceof Long) {
+ return (Long) o;
+ } else if (o == null) {
+ return null;
+ } else {
+ try {
+ return Long.parseLong(o.toString());
+ } catch (NumberFormatException e) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "cannot convert '" + o.toString()
+ + "' to Long");
+ }
+ }
+ }
+
+ /**
+ * Get the value of a required long field. This method throws
+ * MissingInjectedFieldException if the expression is null, and
+ * BadInjectedFieldException if the expression evaluates to a null
+ * value or a value that cannot be coerced to a long.
+ *
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @return the field value
+ */
+ protected Long getLongField(Expression expression,
+ DelegateExecution execution, String fieldName) {
+ Object o = getFieldImpl(expression, execution, fieldName, false);
+ if (o instanceof Long) {
+ return (Long) o;
+ } else {
+ try {
+ return Long.parseLong(o.toString());
+ } catch (NumberFormatException e) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "cannot convert '" + o.toString()
+ + "' to Long");
+ }
+ }
+ }
+
+ /**
+ * Common implementation for field "getter" methods.
+ * @param expression the expression
+ * @param execution the execution
+ * @param fieldName the field name (for logging and exceptions)
+ * @param optional true if the field is optional
+ * @return the field value, possibly null
+ */
+ private Object getFieldImpl(Expression expression,
+ DelegateExecution execution, String fieldName, boolean optional) {
+ if (expression == null) {
+ if (!optional) {
+ throw new MissingInjectedFieldException(
+ fieldName, getTaskName());
+ }
+ return null;
+ }
+
+ Object value;
+
+ try {
+ value = expression.getValue(execution);
+ } catch (Exception e) {
+ if (!optional) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), e.getClass().getSimpleName(), e);
+ }
+
+ // At this point, we have an exception that occurred while
+ // evaluating an expression for an optional field. A common
+ // problem is that the expression is a simple reference to a
+ // variable which has never been set, e.g. the expression is
+ // ${x}. The normal activiti behavior is to throw an exception,
+ // but we don't like that, so we have the following workaround,
+ // which parses the expression text to see if it is a "simple"
+ // variable reference, and if so, returns null. If the
+ // expression is anything other than a single variable
+ // reference, then an exception is thrown, as it would have
+ // been without this workaround.
+
+ // Get the expression text so we can parse it
+ String s = expression.getExpressionText();
+
+// if (isDebugEnabled(execution)) {
+// logDebug(execution, getTaskName() + " field '" + fieldName
+// + "' expression evaluation failed: " + s);
+// }
+
+ int len = s.length();
+ int i = 0;
+
+ // Skip whitespace
+ while (i < len && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+
+ // Next character must be '$'
+ if (i == len || s.charAt(i++) != '$') {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), e.getClass().getSimpleName(), e);
+ }
+
+ // Skip whitespace
+ while (i < len && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+
+ // Next character must be '{'
+ if (i == len || s.charAt(i++) != '{') {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), e.getClass().getSimpleName(), e);
+ }
+
+ // Skip whitespace
+ while (i < len && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+
+ // Collect the variable name
+ StringBuilder variable = new StringBuilder();
+ while (i < len && isWordCharacter(s.charAt(i))) {
+ variable.append(s.charAt(i));
+ i++;
+ }
+
+ if (variable.length() == 0) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), e.getClass().getSimpleName(), e);
+ }
+
+ // Skip whitespace
+ while (i < len && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+
+ // Next character must be '}'
+ if (i == len || s.charAt(i++) != '}') {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), e.getClass().getSimpleName(), e);
+ }
+
+ // Skip whitespace
+ while (i < len && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+
+ // Must be at end of string
+ if (i != len) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), e.getClass().getSimpleName(), e);
+ }
+
+// if (isDebugEnabled(execution)) {
+// logDebug(execution, "Checking if variable '"
+// + variable.toString() + "' exists");
+// }
+
+ // If the variable exists then the problem was
+ // something else...
+ if (execution.hasVariable(variable.toString())) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), e.getClass().getSimpleName(), e);
+ }
+
+ // The variable doesn't exist.
+
+// if (isDebugEnabled(execution)) {
+// logDebug(execution, "Variable '" + variable.toString()
+// + "' does not exist [ok]");
+// }
+
+ value = null;
+ }
+
+ if (value == null && !optional) {
+ throw new BadInjectedFieldException(
+ fieldName, getTaskName(), "required field has null value");
+ }
+
+ return value;
+ }
+
+ /**
+ * Tests if a character is a "word" character.
+ * @param c the character
+ * @return true if the character is a "word" character.
+ */
+ private boolean isWordCharacter(char c) {
+ return (Character.isLetterOrDigit(c) || c == '_');
+ }
+
+ /**
+ * Tests if the specified string is a legal flow variable name.
+ * @param name the string
+ * @return true if the string is a legal flow variable name
+ */
+ private boolean isLegalVariable(String name) {
+ if (name == null) {
+ return false;
+ }
+
+ int len = name.length();
+
+ if (len == 0) {
+ return false;
+ }
+
+ char c = name.charAt(0);
+
+ if (!Character.isLetter(c) && c != '_') {
+ return false;
+ }
+
+ for (int i = 1; i < len; i++) {
+ c = name.charAt(i);
+ if (!Character.isLetterOrDigit(c) && c != '_') {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the name of the task (normally the java class name).
+ * @return the name of the task
+ */
+ public String getTaskName() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/HealthCheckHandler.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/HealthCheckHandler.java
new file mode 100644
index 0000000000..df6213284c
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/HealthCheckHandler.java
@@ -0,0 +1,203 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Map;
+import java.util.UUID;
+
+import org.openecomp.mso.logger.MsoLogger;
+import org.openecomp.mso.logger.MessageEnum;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+
+import org.camunda.bpm.engine.ProcessEngines;
+
+@Path("/")
+public class HealthCheckHandler {
+
+ private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+ private static final String SITENAME = "mso.sitename";
+ private static final String ADPTER_ENDPOINT = "mso.adapters.db.endpoint";
+ private static final String CONFIG = "mso.bpmn.urn.properties";
+
+ private static final String CHECK_HTML = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Health Check</title></head><body>Application ready</body></html>";
+ private static final String NOT_FOUND = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Application Not Started</title></head><body>Application not started. Properties file missing or invalid or database Connection failed</body></html>";
+ private static final String NOT_HEALTHY = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Application Not Started</title></head><body>Application not available or at least one of the sub-modules is not available.</body></html>";
+ public static final Response HEALTH_CHECK_RESPONSE = Response.status (HttpStatus.SC_OK)
+ .entity (CHECK_HTML)
+ .build ();
+ public static final Response HEALTH_CHECK_NOK_RESPONSE = Response.status (HttpStatus.SC_SERVICE_UNAVAILABLE)
+ .entity (NOT_HEALTHY)
+ . build ();
+ public static final Response NOT_STARTED_RESPONSE = Response.status (HttpStatus.SC_SERVICE_UNAVAILABLE)
+ .entity (NOT_FOUND)
+ .build ();
+
+ @HEAD
+ @GET
+ @Path("/healthcheck")
+ @Produces("text/html")
+ public Response healthcheck (@QueryParam("requestId") String requestId) {
+ MsoLogger.setServiceName ("Healthcheck");
+ verifyOldUUID(requestId);
+
+ PropertyConfiguration propertyConfiguration = PropertyConfiguration.getInstance();
+ Map<String,String> props = propertyConfiguration.getProperties(CONFIG);
+
+ if (props == null) {
+
+ msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.AvailabilityError, "Unable to load " + CONFIG);
+
+ return NOT_STARTED_RESPONSE;
+ }
+
+ String siteName = props.get(SITENAME);
+ String endpoint = props.get(ADPTER_ENDPOINT);
+
+ if (null == siteName || siteName.length () == 0 || null == endpoint || endpoint.length () == 0) {
+
+ msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError, "Unable to load key attributes (" + SITENAME + " or " + ADPTER_ENDPOINT + ") from the config file:" + CONFIG);
+
+ return NOT_STARTED_RESPONSE;
+ }
+
+ try {
+ if (!this.getSiteStatus (endpoint, siteName)) {
+ msoLogger.debug("This site is currently disabled for maintenance.");
+ return HEALTH_CHECK_NOK_RESPONSE;
+ }
+ } catch (Exception e) {
+
+ msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Exception while getting SiteStatus", e);
+
+ msoLogger.debug("Exception while getting SiteStatus");
+ return NOT_STARTED_RESPONSE;
+ }
+
+ try {
+ ProcessEngines.getDefaultProcessEngine().getIdentityService().createGroupQuery().list();
+ } catch (final Exception e) {
+
+ msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Exception while verifying Camunda engine", e);
+
+ msoLogger.debug("Exception while verifying Camunda engine");
+ return NOT_STARTED_RESPONSE;
+ }
+
+ return HEALTH_CHECK_RESPONSE;
+ }
+
+
+ private String verifyOldUUID (String oldId) {
+ if (!isValidUUID(oldId)) {
+ String newId = UUID.randomUUID().toString();
+ MsoLogger.setLogContext(newId, null);
+ return newId;
+ }
+ MsoLogger.setLogContext(oldId, null);
+ return oldId;
+ }
+
+
+ private boolean isValidUUID (String id) {
+ try {
+ if (null == id) {
+ return false;
+ }
+ UUID uuid = UUID.fromString(id);
+ return uuid.toString().equalsIgnoreCase(id);
+ } catch (IllegalArgumentException iae) {
+ return false;
+ }
+ }
+
+ private boolean getSiteStatus (String url, String site) throws Exception {
+ HttpResponse response;
+ // set the connection timeout value to 30 seconds (30000 milliseconds)
+ RequestConfig.Builder requestBuilder = RequestConfig.custom();
+ requestBuilder = requestBuilder.setConnectTimeout(30000);
+ requestBuilder = requestBuilder.setConnectionRequestTimeout(30000);
+ HttpClientBuilder builder = HttpClientBuilder.create ();
+ builder.setDefaultRequestConfig (requestBuilder.build ());
+
+ HttpPost post = new HttpPost(url);
+ msoLogger.debug("Post url is: " + url);
+
+ //now create a soap request message as follows:
+ final StringBuffer payload = new StringBuffer();
+ payload.append("\n");
+ payload.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:req=\"http://com.att.mso/requestsdb\">\n");
+ payload.append("<soapenv:Header/>\n");
+ payload.append("<soapenv:Body>\n");
+ payload.append("<req:getSiteStatus>\n");
+ payload.append("<siteName>" + site + "</siteName>\n");
+ payload.append("</req:getSiteStatus>\n");
+ payload.append("</soapenv:Body>\n");
+ payload.append("</soapenv:Envelope>\n");
+
+ msoLogger.debug ("Initialize SOAP request to url:" + url);
+ msoLogger.debug ("The payload of the request is:" + payload);
+ HttpEntity entity = new StringEntity(payload.toString(),"UTF-8");
+ post.setEntity(entity);
+
+ try (CloseableHttpClient client = builder.build()) {
+ response = client.execute(post);
+ msoLogger.debug("Response received is:" + response);
+
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != 200) {
+
+ msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError,
+ "Communication with DB Adapter failed, The response received from DB Adapter is with failed status code:" + statusCode);
+
+ Exception e = new Exception("Communication with DB Adapter failed");
+ throw e;
+ }
+ BufferedReader rd = new BufferedReader(
+ new InputStreamReader(response.getEntity().getContent()));
+
+ StringBuffer result = new StringBuffer();
+ String line = "";
+ while ((line = rd.readLine()) != null) {
+ result.append(line);
+ }
+ msoLogger.debug("Content of the response is:" + result);
+ String status = result.substring(result.indexOf("<return>") + 8, result.indexOf("</return>"));
+
+ return Boolean.valueOf(status);
+ }
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/LogTask.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/LogTask.java
new file mode 100644
index 0000000000..41033d9e12
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/LogTask.java
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+import org.camunda.bpm.engine.delegate.Expression;
+
+import org.openecomp.mso.logger.MsoAlarmLogger;
+import org.openecomp.mso.logger.MsoLogger;
+
+/**
+ * Logs a text message. The text may contain variable references.
+ * For example:<br/><br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;name=$name, address=$address
+ * <p>
+ * Required fields:<br/><br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;text: The text to log<br/>
+ * Optional fields:<br/><br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;level: The log level (TRACE, DEBUG, INFO, WARN, ERROR)<br/>
+ */
+public class LogTask extends BaseTask {
+
+
+ private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+ private static MsoAlarmLogger alarmLogger = new MsoAlarmLogger();
+
+ private Expression text;
+ private Expression level;
+
+ public void execute(DelegateExecution execution) throws Exception {
+ String theText = getStringField(text, execution, "text");
+
+
+
+ StringBuilder out = new StringBuilder();
+ StringBuilder var = new StringBuilder();
+ boolean inVar = false;
+
+ int pos = 0;
+ int len = theText.length();
+
+ while (pos < len) {
+ char c = theText.charAt(pos++);
+
+ if (inVar && !Character.isLetterOrDigit(c) && c != '_') {
+ if (var.length() > 0) {
+ Object value = execution.getVariable(var.toString());
+
+ if (value != null) {
+ out.append(value.toString());
+ }
+
+ var.setLength(0);
+ }
+
+ inVar = false;
+ }
+
+ if (c == '$') {
+ inVar = true;
+ } else {
+ if (inVar) {
+ var.append(c);
+ } else {
+ out.append(c);
+ }
+ }
+ }
+
+ if (inVar && var.length() > 0) {
+ Object value = execution.getVariable(var.toString());
+ if (value != null) {
+ out.append(value.toString());
+ }
+ }
+
+
+
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/MissingInjectedFieldException.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/MissingInjectedFieldException.java
new file mode 100644
index 0000000000..589a111b3b
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/MissingInjectedFieldException.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+/**
+ * A BadInjectedFieldException that indicates a required field is missing.
+ */
+public class MissingInjectedFieldException extends BadInjectedFieldException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * @param fieldName the field name
+ * @param taskName the task name
+ */
+ public MissingInjectedFieldException(String fieldName, String taskName) {
+ super(fieldName, taskName, "missing required field");
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/PropertyConfiguration.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/PropertyConfiguration.java
new file mode 100644
index 0000000000..90df1da7e5
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/PropertyConfiguration.java
@@ -0,0 +1,440 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.slf4j.MDC;
+
+import org.openecomp.mso.logger.MessageEnum;
+import org.openecomp.mso.logger.MsoLogger;
+
+/**
+ * Loads the property configuration from file system and refreshes the
+ * properties when the property gets changed.
+ *
+ * WARNING: automatic refreshes might not work on network filesystems.
+ */
+public class PropertyConfiguration {
+
+ /**
+ * The base name of the MSO BPMN properties file (mso.bpmn.properties).
+ */
+ public static final String MSO_BPMN_PROPERTIES = "mso.bpmn.properties";
+
+ /**
+ * The base name of the MSO BPMN URN-Mappings properties file (mso.bpmn.urn.properties).
+ */
+ public static final String MSO_BPMN_URN_PROPERTIES = "mso.bpmn.urn.properties";
+
+ /**
+ * The name of the meta-property holding the time the properties were loaded
+ * from the file.
+ */
+ public static final String TIMESTAMP_PROPERTY = "mso.properties.timestamp";
+
+ private static final MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+
+ private static final List<String> SUPPORTED_FILES =
+ Arrays.asList(MSO_BPMN_PROPERTIES, MSO_BPMN_URN_PROPERTIES);
+
+ private volatile String msoConfigPath = null;
+
+ private final ConcurrentHashMap<String, Map<String, String>> propFileCache =
+ new ConcurrentHashMap<String, Map<String, String>>();
+
+ private final Object CACHELOCK = new Object();
+ private FileWatcherThread fileWatcherThread = null;
+
+ // The key is the file name
+ private Map<String, TimerTask> timerTaskMap = new HashMap<String, TimerTask>();
+
+ /**
+ * Singleton holder pattern eliminates locking when accessing the instance
+ * and still provides for lazy initialization.
+ */
+ private static class PropertyConfigurationInstanceHolder {
+ private static PropertyConfiguration instance = new PropertyConfiguration();
+ }
+
+ /**
+ * Gets the one and only instance of this class.
+ */
+ public static PropertyConfiguration getInstance() {
+ return PropertyConfigurationInstanceHolder.instance;
+ }
+
+ /**
+ * Returns the list of supported files.
+ */
+ public static List<String> supportedFiles() {
+ return new ArrayList<String>(SUPPORTED_FILES);
+ }
+
+ /**
+ * Private Constructor.
+ */
+ private PropertyConfiguration() {
+ startUp();
+ }
+
+ /**
+ * May be called to restart the PropertyConfiguration if it was previously shut down.
+ */
+ public synchronized void startUp() {
+ msoConfigPath = System.getProperty("mso.config.path");
+
+ if (msoConfigPath == null) {
+ LOGGER.debug("mso.config.path JVM system property is not set");
+ return;
+ }
+
+ try {
+ Path directory = FileSystems.getDefault().getPath(msoConfigPath);
+ WatchService watchService = FileSystems.getDefault().newWatchService();
+ directory.register(watchService, ENTRY_MODIFY);
+
+ LOGGER.info(MessageEnum.BPMN_GENERAL_INFO, "BPMN", "Starting FileWatcherThread");
+ LOGGER.debug("Starting FileWatcherThread");
+ fileWatcherThread = new FileWatcherThread(watchService);
+ fileWatcherThread.start();
+ } catch (Exception e) {
+ LOGGER.debug("Error occurred while starting FileWatcherThread:", e);
+ LOGGER.error(
+ MessageEnum.BPMN_GENERAL_EXCEPTION,
+ "BPMN",
+ "Property Configuration",
+ MsoLogger.ErrorCode.UnknownError,
+ "Error occurred while starting FileWatcherThread:" + e);
+ }
+ }
+
+ /**
+ * May be called to shut down the PropertyConfiguration. A shutDown followed
+ * by a startUp will reset the PropertyConfiguration to its initial state.
+ */
+ public synchronized void shutDown() {
+ if (fileWatcherThread != null) {
+ LOGGER.debug("Shutting down FileWatcherThread " + System.identityHashCode(fileWatcherThread));
+ fileWatcherThread.shutdown();
+
+ long waitInSeconds = 10;
+
+ try {
+ fileWatcherThread.join(waitInSeconds * 1000);
+ } catch (InterruptedException e) {
+ LOGGER.debug("FileWatcherThread " + System.identityHashCode(fileWatcherThread)
+ + " shutdown did not occur within " + waitInSeconds + " seconds");
+ }
+
+ LOGGER.debug("Finished shutting down FileWatcherThread " + System.identityHashCode(fileWatcherThread));
+ fileWatcherThread = null;
+ }
+
+ clearCache();
+ msoConfigPath = null;
+ }
+
+ public synchronized boolean isFileWatcherRunning() {
+ return fileWatcherThread != null;
+ }
+
+ public void clearCache() {
+ synchronized(CACHELOCK) {
+ propFileCache.clear();
+ }
+ }
+
+ public int cacheSize() {
+ return propFileCache.size();
+ }
+
+ // TODO: throw IOException?
+ public Map<String, String> getProperties(String fileName) {
+ Map<String, String> properties = propFileCache.get(fileName);
+
+ if (properties == null) {
+ if (!SUPPORTED_FILES.contains(fileName)) {
+ throw new IllegalArgumentException("Not a supported property file: " + fileName);
+ }
+
+ if (msoConfigPath == null) {
+ LOGGER.debug("mso.config.path JVM system property must be set to load " + fileName);
+
+ LOGGER.error(
+ MessageEnum.BPMN_GENERAL_EXCEPTION,
+ "BPMN",
+ MDC.get(fileName),
+ MsoLogger.ErrorCode.UnknownError,
+ "mso.config.path JVM system property must be set to load " + fileName);
+
+ return null;
+ }
+
+ try {
+ properties = readProperties(new File(msoConfigPath, fileName));
+ } catch (Exception e) {
+ LOGGER.debug("Error loading " + fileName);
+
+ LOGGER.error(
+ MessageEnum.BPMN_GENERAL_EXCEPTION,
+ "BPMN",
+ MDC.get(fileName),
+ MsoLogger.ErrorCode.UnknownError,
+ "Error loading " + fileName, e);
+
+ return null;
+ }
+ }
+
+ return Collections.unmodifiableMap(properties);
+ }
+
+ /**
+ * Reads properties from the specified file, updates the property file cache, and
+ * returns the properties in a map.
+ * @param file the file to read
+ * @param reload true if this is a reload event
+ * @return a map of properties
+ */
+ private Map<String, String> readProperties(File file) throws IOException {
+ String fileName = file.getName();
+ LOGGER.debug("Reading " + fileName);
+
+ Map<String, String> properties = new HashMap<String, String>();
+ Properties newProperties = new Properties();
+
+ FileReader reader = null;
+ try {
+ reader = new FileReader(file);
+ newProperties.load(reader);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ LOGGER.debug("Closed " + fileName);
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ }
+
+ for (Entry<Object, Object> entry : newProperties.entrySet()) {
+ properties.put(entry.getKey().toString(), entry.getValue().toString());
+ }
+
+ properties.put(TIMESTAMP_PROPERTY, String.valueOf(System.currentTimeMillis()));
+
+ synchronized(CACHELOCK) {
+ propFileCache.put(fileName, properties);
+ }
+
+ return properties;
+ }
+
+ /**
+ * File watcher thread which monitors a directory for file modification.
+ */
+ private class FileWatcherThread extends Thread {
+ private final WatchService watchService;
+ private final Timer timer = new Timer("FileWatcherTimer");
+
+ public FileWatcherThread(WatchService service) {
+ this.watchService = service;
+ }
+
+ public void shutdown() {
+ interrupt();
+ }
+
+ public void run() {
+ LOGGER.info(MessageEnum.BPMN_GENERAL_INFO, "BPMN",
+ "FileWatcherThread started");
+
+ LOGGER.debug("Started FileWatcherThread " + System.identityHashCode(fileWatcherThread));
+
+ try {
+ WatchKey watchKey = null;
+
+ while (!isInterrupted()) {
+ try {
+ if (watchKey != null) {
+ watchKey.reset();
+ }
+
+ watchKey = watchService.take();
+
+ for (WatchEvent<?> event : watchKey.pollEvents()) {
+ @SuppressWarnings("unchecked")
+ WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
+
+ if ("EVENT_OVERFLOW".equals(pathEvent.kind())) {
+ LOGGER.debug("Ignored overflow event for " + msoConfigPath);
+ continue;
+ }
+
+ String fileName = pathEvent.context().getFileName().toString();
+
+ if (!SUPPORTED_FILES.contains(fileName)) {
+ LOGGER.debug("Ignored modify event for " + fileName);
+ continue;
+ }
+
+ LOGGER.debug("Configuration file has changed: " + fileName);
+
+ LOGGER.info(MessageEnum.BPMN_GENERAL_INFO, "BPMN",
+ "Configuation file has changed: " + fileName);
+
+ // There's a potential problem here. The MODIFY event is
+ // triggered as soon as somebody starts writing the file but
+ // there's no obvious way to know when the write is done. If we
+ // read the file while the write is still in progress, then the
+ // cache can really be messed up. As a workaround, we use a timer
+ // to sleep for at least one second, and then we sleep for as long
+ // as it takes for the file's lastModified time to stop changing.
+ // The timer has another benefit: it consolidates multiple events
+ // that we seem to receive when a file is modified.
+
+ synchronized(timerTaskMap) {
+ TimerTask task = timerTaskMap.get(fileName);
+
+ if (task != null) {
+ task.cancel();
+ }
+
+ File file = new File(msoConfigPath, fileName);
+ task = new DelayTimerTask(timer, file, 1000);
+ timerTaskMap.put(fileName, task);
+ }
+ }
+ } catch (InterruptedException e) {
+ break;
+ } catch (ClosedWatchServiceException e) {
+ LOGGER.info(
+ MessageEnum.BPMN_GENERAL_INFO,
+ "BPMN",
+ "FileWatcherThread shut down because the watch service was closed");
+ break;
+ } catch (Exception e) {
+ LOGGER.error(
+ MessageEnum.BPMN_GENERAL_EXCEPTION,
+ "BPMN",
+ "Property Configuration",
+ MsoLogger.ErrorCode.UnknownError,
+ "FileWatcherThread caught unexpected " + e.getClass().getSimpleName(), e);
+ }
+
+ }
+ } finally {
+ timer.cancel();
+
+ synchronized(timerTaskMap) {
+ timerTaskMap.clear();
+ }
+
+ try {
+ watchService.close();
+ } catch (IOException e) {
+ LOGGER.debug("FileWatcherThread caught " + e.getClass().getSimpleName()
+ + " while closing the watch service");
+ }
+
+ LOGGER.info(MessageEnum.BPMN_GENERAL_INFO, "BPMN",
+ "FileWatcherThread stopped");
+ }
+ }
+ }
+
+ private class DelayTimerTask extends TimerTask {
+ private final File file;
+ private final long lastModifiedTime;
+ private final Timer timer;
+
+ public DelayTimerTask(Timer timer, File file, long delay) {
+ this.timer = timer;
+ this.file = file;
+ this.lastModifiedTime = file.lastModified();
+ timer.schedule(this, delay);
+ }
+
+ @Override
+ public void run() {
+ try {
+ long newLastModifiedTime = file.lastModified();
+
+ if (newLastModifiedTime == lastModifiedTime) {
+ try {
+ readProperties(file);
+ } catch (Exception e) {
+ LOGGER.error(
+ MessageEnum.BPMN_GENERAL_EXCEPTION,
+ "BPMN",
+ "Property Configuration",
+ MsoLogger.ErrorCode.UnknownError,
+ "Unable to reload " + file, e);
+ }
+ } else {
+ LOGGER.debug("Delaying reload of " + file + " by 1 second");
+
+ synchronized(timerTaskMap) {
+ TimerTask task = timerTaskMap.get(file.getName());
+
+ if (task != null && task != this) {
+ task.cancel();
+ }
+
+ task = new DelayTimerTask(timer, file, 1000);
+ timerTaskMap.put(file.getName(), task);
+ }
+ }
+ } finally {
+ synchronized(timerTaskMap) {
+ TimerTask task = timerTaskMap.get(file.getName());
+
+ if (task == this) {
+ timerTaskMap.remove(file.getName());
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ReadConfigTask.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ReadConfigTask.java
new file mode 100644
index 0000000000..b46ffcd7f7
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ReadConfigTask.java
@@ -0,0 +1,111 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.camunda.bpm.engine.ProcessEngineException;
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+import org.camunda.bpm.engine.delegate.Expression;
+
+import org.openecomp.mso.logger.MsoLogger;
+
+/**
+ * Reads the contents of a resource file as a string and stores it in an
+ * execution variable.
+ * <p>
+ * Required fields:<br/><br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;file: the resource file path<br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;outputVariable: the output variable name<br/>
+ */
+public class ReadConfigTask extends BaseTask {
+
+ private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+ private static Properties properties = null;
+
+ private Expression propertiesFile;
+
+ public void execute(DelegateExecution execution) throws Exception {
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("Started Executing " + getTaskName());
+ }
+
+ String thePropertiesFile =
+ getStringField(propertiesFile, execution, "propertiesFile");
+
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("propertiesFile = " + thePropertiesFile);
+ }
+
+ Boolean shouldFail = (Boolean) execution.getVariable("shouldFail");
+
+ if (shouldFail != null && shouldFail) {
+ throw new ProcessEngineException(getClass().getSimpleName() + " Failed");
+ }
+
+ synchronized (ReadConfigTask.class) {
+ if (properties == null) {
+ properties = new Properties();
+
+ InputStream stream = null;
+
+ try {
+ stream = getClass().getResourceAsStream(thePropertiesFile);
+
+ if (stream == null) {
+ throw new IOException("Resource not found: " + thePropertiesFile);
+ }
+
+ properties.load(stream);
+
+ stream.close();
+ stream = null;
+
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (Exception e) {
+ // Do nothing
+ }
+ }
+ }
+ }
+ }
+
+ for (Object objectKey : properties.keySet()) {
+ String key = (String) objectKey;
+ String value = properties.getProperty(key);
+
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("Setting variable '" + key + "' to '" + value + "'");
+ }
+
+ execution.setVariable(key, value);
+ }
+
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("Done Executing " + getTaskName());
+ }
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ReadFileTask.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ReadFileTask.java
new file mode 100644
index 0000000000..389fdc0518
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ReadFileTask.java
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.camunda.bpm.engine.ProcessEngineException;
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+import org.camunda.bpm.engine.delegate.Expression;
+
+import org.openecomp.mso.logger.MsoLogger;
+
+/**
+ * Conditionally reads the contents of a resource file as a string and stores it
+ * in an execution variable. The file is read only if the value of the input
+ * variable is null.
+ * <p>
+ * Required fields:<br/><br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;file: the resource file path<br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;inputVariable: the input variable name<br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;outputVariable: the output variable name<br/>
+ */
+public class ReadFileTask extends BaseTask {
+
+ private Expression file;
+ private Expression inputVariable;
+ private Expression outputVariable;
+
+ private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+
+ public void execute(DelegateExecution execution) throws Exception {
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("Started Executing " + getTaskName());
+ }
+
+ String theInputVariable =
+ getStringField(inputVariable, execution, "inputVariable");
+ String theOutputVariable =
+ getOutputField(outputVariable, execution, "outputVariable");
+ String theFile =getStringField(file, execution, "file");
+
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("inputVariable = " + theInputVariable
+ + " outputVariable = " + theOutputVariable
+ + "file = " + theFile);
+ }
+
+ Boolean shouldFail = (Boolean) execution.getVariable("shouldFail");
+
+ if (shouldFail != null && shouldFail) {
+ throw new ProcessEngineException(getClass().getSimpleName() + " Failed");
+ }
+
+ Object value = execution.getVariable(theInputVariable);
+
+ if (value == null) {
+ InputStream xmlStream = null;
+
+ try {
+ xmlStream = getClass().getResourceAsStream(theFile);
+
+ if (xmlStream == null) {
+ throw new IOException("Resource not found: " + theFile);
+ }
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(xmlStream));
+ StringBuilder output = new StringBuilder();
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ output.append(line);
+ }
+
+ xmlStream.close();
+ xmlStream = null;
+
+ value = output.toString();
+
+ } finally {
+ if (xmlStream != null) {
+ try {
+ xmlStream.close();
+ } catch (Exception e) {
+ // Do nothing
+ }
+ }
+ }
+ }
+ execution.setVariable(theInputVariable, value);
+ execution.setVariable(theOutputVariable, value);
+ System.out.println("ServiceInput - " + execution.getVariable("gServiceInput"));
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("Done Executing " + getTaskName());
+ }
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ResponseBuilder.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ResponseBuilder.java
new file mode 100644
index 0000000000..632933d3a3
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/ResponseBuilder.java
@@ -0,0 +1,297 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+
+/**
+ * Used in the output variable mapping configuration of subflow call activity
+ * tasks to normalize subflow responses. The output mapping is normally set up
+ * as follows. Note that the order of these mappings is important!
+ * <p>
+ * OUTPUT MAPPING
+ * <pre>
+ * SOURCE EXPRESSION TARGET
+ * ${ResponseBuilder.buildWorkflowException(execution)} WorkflowException
+ * ${ResponseBuilder.buildWorkflowResponse(execution)} SomeResponseVariable
+ * </pre>
+ */
+public class ResponseBuilder implements java.io.Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a WorkflowException using data from the execution variables.
+ * If the variables do not indicate that there was an error, null
+ * is returned.
+ * @param execution the execution
+ */
+ public WorkflowException buildWorkflowException(DelegateExecution execution) {
+
+ String method = getClass().getSimpleName() + ".buildWorkflowException(" +
+ "execution=" + execution.getId() +
+ ")";
+ String isDebugLogEnabled = (String) execution.getVariable("isDebugLogEnabled");
+ logDebug("Entered " + method, isDebugLogEnabled);
+
+ String prefix = (String) execution.getVariable("prefix");
+ String processKey = getProcessKey(execution);
+
+ logDebug("processKey=" + processKey, isDebugLogEnabled);
+
+ // See if there"s already a WorkflowException object in the execution.
+ WorkflowException theException = (WorkflowException) execution.getVariable("WorkflowException");
+
+ if (theException != null) {
+ logDebug("Exited " + method + " - propagated " + theException, isDebugLogEnabled);
+ return theException;
+ }
+
+ // Look in the legacy variables: ErrorResponse and ResponseCode
+
+ String errorResponse = trimString(execution.getVariable(prefix + "ErrorResponse"), null);
+ String responseCode = trimString(execution.getVariable(prefix + "ResponseCode"), null);
+ logDebug("errorResponse=" + errorResponse, isDebugLogEnabled);
+ logDebug("responseCode=" + responseCode, isDebugLogEnabled);
+ if (errorResponse != null || !isOneOf(responseCode, null, "0", "200", "201", "202", "204")) {
+ // This is an error condition. We need to return a WorkflowExcpetion
+
+ if (errorResponse == null) {
+ // No errorResponse string. See if there"s something in the Response variable
+ String response = trimString(execution.getVariable(processKey + "Response"), null);
+ if (response == null) {
+ errorResponse = "Received response code " + responseCode + " from " + processKey;
+ } else {
+ errorResponse = response;
+ }
+ }
+
+ // Some subflows may try to return a WorkflowException as XML in the
+ // errorResponse. If provided, use the errorCode and errorMessage
+ // from the XML
+
+ String maybeXML = removeXMLNamespaces(errorResponse);
+
+ String xmlErrorMessage = trimString(getXMLTextElement(maybeXML, "ErrorMessage"), null);
+ String xmlErrorCode = trimString(getXMLTextElement(maybeXML, "ErrorCode"), null);
+
+ if (xmlErrorMessage != null || xmlErrorCode != null) {
+ logDebug("xmlErrorMessage=" + xmlErrorMessage, isDebugLogEnabled);
+ logDebug("xmlErrorCode=" + xmlErrorCode, isDebugLogEnabled);
+
+ if (xmlErrorMessage == null) {
+ errorResponse = "Received error code " + xmlErrorCode + " from " + processKey;
+ } else {
+ errorResponse = xmlErrorMessage;
+ }
+
+ if (xmlErrorCode != null) {
+ responseCode = xmlErrorCode;
+ }
+ }
+
+ // Convert the responseCode to an integer
+
+ int intResponseCode;
+
+ try {
+ intResponseCode = Integer.valueOf(responseCode);
+ } catch (NumberFormatException e) {
+ // Internal Error
+ intResponseCode = 2000;
+ }
+
+ // Convert 3-digit HTTP response codes (we should not be using them here)
+ // to appropriate 4-digit response codes
+
+ if (intResponseCode < 1000) {
+ if (intResponseCode >= 400 && intResponseCode <= 499) {
+ // Invalid Message
+ intResponseCode = 1002;
+ } else {
+ // Internal Error
+ intResponseCode = 2000;
+ }
+ }
+
+ // Create a new WorkflowException object
+
+ theException = new WorkflowException(processKey, intResponseCode, errorResponse);
+ execution.setVariable("WorkflowException", theException);
+ logDebug("Exited " + method + " - created " + theException, isDebugLogEnabled);
+ return theException;
+ }
+
+ logDebug("Exited " + method + " - no WorkflowException", isDebugLogEnabled);
+ return null;
+ }
+
+ /**
+ * Returns the "Response" variable, unless the execution variables
+ * indicate there was an error. In that case, null is returned.
+ * @param execution the execution
+ */
+ public Object buildWorkflowResponse(DelegateExecution execution) {
+
+ String method = getClass().getSimpleName() + ".buildWorkflowResponse(" +
+ "execution=" + execution.getId() +
+ ")";
+ String isDebugLogEnabled = (String) execution.getVariable("isDebugLogEnabled");
+ logDebug("Entered " + method, isDebugLogEnabled);
+
+ String prefix = (String) execution.getVariable("prefix");
+ String processKey = getProcessKey(execution);
+
+ Object theResponse = null;
+
+ WorkflowException theException = (WorkflowException) execution.getVariable("WorkflowException");
+ String errorResponse = trimString(execution.getVariable(prefix + "ErrorResponse"), null);
+ String responseCode = trimString(execution.getVariable(prefix + "ResponseCode"), null);
+
+ if (theException == null && errorResponse == null &&
+ isOneOf(responseCode, null, "0", "200", "201", "202", "204")) {
+
+ theResponse = execution.getVariable("WorkflowResponse");
+
+ if (theResponse == null) {
+ theResponse = execution.getVariable(processKey + "Response");
+ }
+ }
+
+ logDebug("Exited " + method, isDebugLogEnabled);
+ return theResponse;
+ }
+
+ /**
+ * Checks if the specified item is one of the specified values.
+ * @param item the item
+ * @param values the list of values
+ * @return true if the item is in the list of values
+ */
+ private boolean isOneOf(Object item, Object ... values) {
+ if (values == null) {
+ return item == null;
+ }
+
+ for (Object value : values) {
+ if (value == null) {
+ if (item == null) {
+ return true;
+ }
+ } else {
+ if (value.equals(item)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Creates a string value of the specified object, trimming whitespace in
+ * the process. If the result is null or empty, the specified empty string
+ * value is returned. Otherwise the trimmed value is returned. This method
+ * helps ensure consistent treatment of empty and null strings.
+ * @param object the object to convert (possibly null)
+ * @param emptyStringValue the desired value for empty results
+ */
+ private String trimString(Object object, String emptyStringValue) {
+ if (object == null) {
+ return emptyStringValue;
+ }
+
+ String s = String.valueOf(object).trim();
+ return s.equals("") ? emptyStringValue : s;
+ }
+
+ /**
+ * Returns the process definition key (i.e. the process name) from the
+ * execution.
+ * @param execution the execution
+ */
+ private String getProcessKey(DelegateExecution execution) {
+ Object testKey = execution.getVariable("testProcessKey");
+
+ if (testKey instanceof String) {
+ return (String) testKey;
+ }
+
+ return execution.getProcessEngineServices().getRepositoryService()
+ .getProcessDefinition(execution.getProcessDefinitionId()).getKey();
+ }
+
+ /**
+ * Logs a message at the DEBUG level.
+ * @param message the message
+ * @param isDebugLogEnabled a flag indicating if DEBUG level is enabled
+ */
+ private void logDebug(String message, String isDebugLogEnabled) {
+ BPMNLogger.debug(isDebugLogEnabled, message);
+ }
+
+ /**
+ * Removes namespace definitions and prefixes from XML, if any.
+ */
+ private String removeXMLNamespaces(String xml) {
+ // remove xmlns declaration
+ xml = xml.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
+
+ // remove opening tag prefix
+ xml = xml.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
+
+ // remove closing tags prefix
+ xml = xml.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
+
+ // remove extra spaces left when xmlns declarations are removed
+ xml = xml.replaceAll("\\s+>", ">");
+
+ return xml;
+ }
+
+ /**
+ * Extracts text from an XML element. This method is not namespace aware
+ * (namespaces are ignored). The first matching element is selected.
+ * @param xml the XML document or fragment
+ * @param tag the desired element, e.g. "<name>"
+ * @return the element text, or null if the element was not found
+ */
+ private String getXMLTextElement(String xml, String tag) {
+ xml = removeXMLNamespaces(xml);
+
+ if (!tag.startsWith("<")) {
+ tag = "<" + tag + ">";
+ }
+
+ int start = xml.indexOf(tag);
+
+ if (start == -1) {
+ return null;
+ }
+
+ int end = xml.indexOf('<', start + tag.length());
+
+ if (end == -1) {
+ return null;
+ }
+
+ return xml.substring(start + tag.length(), end);
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/RollbackData.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/RollbackData.java
new file mode 100644
index 0000000000..1b2bb8752c
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/RollbackData.java
@@ -0,0 +1,108 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An object that stores data for rollbacks. Data is organized by type. A
+ * type is simply a string identifier. Multiple types of data may be stored
+ * in the same object for separate rollback operations.
+ */
+public class RollbackData implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private Map<String, Map<String, Serializable>> dictionary =
+ new HashMap<String, Map<String, Serializable>>();
+
+ /**
+ * Returns true if the specified type is stored in this object.
+ * @param type the data type
+ */
+ public boolean hasType(String type) {
+ return dictionary.containsKey(type);
+ }
+
+ /**
+ * Stores a single item.
+ * @param type the data type
+ * @param key the key
+ * @param value the value
+ */
+ public void put(String type, String key, String value) {
+ Map<String, Serializable> mapForType = dictionary.get(type);
+
+ if (mapForType == null) {
+ mapForType = new HashMap<String, Serializable>();
+ dictionary.put(type, mapForType);
+ }
+
+ mapForType.put(key, value);
+ }
+
+ /**
+ * Gets a single item.
+ * @param type the data type
+ * @param key the key
+ * @return the item or null if there is no item for the specified type and key
+ */
+ public Serializable get(String type, String key) {
+ Map<String, Serializable> mapForType = dictionary.get(type);
+
+ if (mapForType == null) {
+ return null;
+ }
+
+ return mapForType.get(key);
+ }
+
+ /**
+ * Gets a map containing all items associated with the specified data type.
+ * @param type the data type
+ * @return a map, or null if there are no items associated with the specified
+ * data type
+ */
+ public Map<String, Serializable> get(String type) {
+ return dictionary.get(type);
+ }
+
+ /**
+ * Returns a string representation of this object.
+ */
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ out.append(getClass().getSimpleName());
+ out.append('[');
+ boolean hasOne = false;
+ for (String type : dictionary.keySet()) {
+ if (hasOne) {
+ out.append(',');
+ }
+ out.append(type);
+ out.append(dictionary.get(type));
+ hasOne = true;
+ }
+ out.append(']');
+ return out.toString();
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/URNMappingsTask.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/URNMappingsTask.java
new file mode 100644
index 0000000000..6c6d96ad2c
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/URNMappingsTask.java
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+
+/**
+ * DEPRECATION WARNING: setting of URN mappings is now done by a plugin.
+ */
+@Deprecated
+public class URNMappingsTask extends BaseTask {
+ public void execute(DelegateExecution execution) throws Exception {
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/WorkflowException.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/WorkflowException.java
new file mode 100644
index 0000000000..559ec6df00
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/WorkflowException.java
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import java.io.Serializable;
+
+/**
+ * An object that represents a workflow exception.
+ */
+public class WorkflowException implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private final String processKey;
+ private final int errorCode;
+ private final String errorMessage;
+
+ /**
+ * Constructor
+ * @param processKey the process key for the process that generated the exception
+ * @param errorCode the numeric error code (normally 1xxx or greater)
+ * @param errorMessage a short error message
+ */
+ public WorkflowException(String processKey, int errorCode,
+ String errorMessage) {
+ this.processKey = processKey;
+ this.errorCode = errorCode;
+ this.errorMessage = errorMessage;
+ }
+
+ /**
+ * Returns the process key.
+ */
+ public String getProcessKey() {
+ return processKey;
+ }
+
+ /**
+ * Returns the error code.
+ */
+ public int getErrorCode() {
+ return errorCode;
+ }
+
+ /**
+ * Returns the error message.
+ */
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ /**
+ * Returns a string representation of this object.
+ */
+ public String toString() {
+ StringBuilder out = new StringBuilder();
+ out.append(getClass().getSimpleName());
+ out.append("[processKey=");
+ out.append(getProcessKey());
+ out.append(",errorCode=");
+ out.append(getErrorCode());
+ out.append(",errorMessage=");
+ out.append(getErrorMessage());
+ out.append("]");
+ return out.toString();
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/XQueryScriptTask.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/XQueryScriptTask.java
new file mode 100644
index 0000000000..8a7b20016b
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/XQueryScriptTask.java
@@ -0,0 +1,243 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.util.Iterator;
+
+import javax.xml.transform.stream.StreamSource;
+
+import org.camunda.bpm.engine.ProcessEngineException;
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+//import java.util.logging.Logger;
+import org.camunda.bpm.engine.delegate.Expression;
+
+import org.openecomp.mso.logger.MessageEnum;
+import org.openecomp.mso.logger.MsoLogger;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.s9api.DocumentBuilder;
+import net.sf.saxon.s9api.Processor;
+import net.sf.saxon.s9api.QName;
+import net.sf.saxon.s9api.XQueryCompiler;
+import net.sf.saxon.s9api.XQueryEvaluator;
+import net.sf.saxon.s9api.XQueryExecutable;
+import net.sf.saxon.s9api.XdmAtomicValue;
+import net.sf.saxon.s9api.XdmItem;
+import net.sf.saxon.s9api.XdmNode;
+
+/**
+ * Executes an XQuery script.
+ * <p>
+ * Required fields:<br/><br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;scriptFile: the XQuery script file path<br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;outputVariable: the output variable name<br/>
+ * <p>
+ * Optional fields:<br/><br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;xmlInputVariables: CSV list of variables containing
+ * XML data to be injected into the script<br/>
+ * &nbsp;&nbsp;&nbsp;&nbsp;atomicInputVariables: CSV list of variables containing
+ * atomic data to be injected into the script<br/>
+ */
+public class XQueryScriptTask extends BaseTask {
+
+ private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+
+ private Expression scriptFile;
+ private Expression xmlInputVariables;
+ private Expression atomicInputVariables;
+ private Expression outputVariable;
+
+ public void execute(DelegateExecution execution) throws Exception {
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("Started Executing " + getTaskName());
+ }
+
+ String theScriptFile =
+ getStringField(scriptFile, execution, "scriptFile");
+ String theXmlInputVariables =
+ getOptionalStringField(xmlInputVariables, execution, "xmlInputVariables");
+ String theAtomicInputVariables =
+ getOptionalStringField(atomicInputVariables, execution, "atomicInputVariables");
+ String theOutputVariable =
+ getStringField(outputVariable, execution, "outputVariable");
+
+ if (msoLogger.isDebugEnabled()) {
+ System.out.println("scriptFile = " + theScriptFile
+ + " xmlInputVariables = " + theXmlInputVariables
+ + " atomicInputVariables = " + theAtomicInputVariables
+ + "outputVariable = " + theOutputVariable);
+ }
+
+ String[] xmlInputVariableArray = (theXmlInputVariables == null)
+ ? new String[0] : theXmlInputVariables.split(",[ ]*");
+
+ String[] atomicInputVariableArray = (theAtomicInputVariables == null)
+ ? new String[0] : theAtomicInputVariables.split(",[ ]*");
+
+ Boolean shouldFail = (Boolean) execution.getVariable("shouldFail");
+
+ if (shouldFail != null && shouldFail) {
+ throw new ProcessEngineException(getClass().getSimpleName() + " Failed");
+ }
+
+ // The script could be compiled once and reused, but we are reading it
+ // and compiling it every time.
+ Configuration configuration = new Configuration();
+ Processor processor = new Processor(configuration);
+ XQueryCompiler compiler = processor.newXQueryCompiler();
+ XQueryExecutable executable = compile(compiler, theScriptFile);
+
+ // The evaluator must not be shared by multiple threads. Here is where
+ // the initial context may be set, as well as values of external variables.
+ XQueryEvaluator evaluator = executable.load();
+
+ // Convert XML string variable content to document-node objects and inject
+ // these into the evaluator. Note: the script must accept the document-node
+ // type. Most MSO scripts today expect element() input, not document-node
+ // input. TODO: figure out how to pass the variable data as element() types.
+
+ for (String xmlInputVariable : xmlInputVariableArray) {
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("Injecting XML variable '" + xmlInputVariable + "'");
+ msoLogger.debug("printing the variable content>>'" + execution.getVariable(xmlInputVariable) +"'");
+ }
+
+ String xml = (String) execution.getVariable(xmlInputVariable);
+ DocumentBuilder documentBuilder = processor.newDocumentBuilder();
+ StreamSource source = new StreamSource(new ByteArrayInputStream(xml.getBytes("UTF-8")));
+ XdmNode xdmNode = documentBuilder.build(source);
+
+ // Inject the document-node object into the XQueryEvaluator.
+ // TODO: transform it to an element()
+ QName variable = new QName(xmlInputVariable);
+ evaluator.setExternalVariable(variable, xdmNode);
+ }
+
+ // Inject atomic variables into the evaluator.
+
+ for (String atomicInputVariable : atomicInputVariableArray) {
+
+ if (msoLogger.isDebugEnabled()) {
+ System.out.println("Injecting object variable '"
+ + atomicInputVariable + "'");
+ }
+
+ QName variable = new QName(atomicInputVariable);
+ Object value = execution.getVariable(atomicInputVariable);
+
+ if (value == null) {
+ // The variable value is null, so we have no way to know what
+ // type it is. I don't know how to deal with this, so for
+ // now, just skip it.
+
+ msoLogger.warn (MessageEnum.BPMN_VARIABLE_NULL, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError, atomicInputVariable);
+
+ continue;
+ }
+
+ // There might be a better way to do this...
+ if (value instanceof BigDecimal) {
+ evaluator.setExternalVariable(variable,
+ new XdmAtomicValue((BigDecimal) value));
+ } else if (value instanceof Boolean) {
+ evaluator.setExternalVariable(variable,
+ new XdmAtomicValue((Boolean) value));
+ } else if (value instanceof Double) {
+ evaluator.setExternalVariable(variable,
+ new XdmAtomicValue((Double) value));
+ } else if (value instanceof Float) {
+ evaluator.setExternalVariable(variable,
+ new XdmAtomicValue((Float) value));
+ } else if (value instanceof Long) {
+ evaluator.setExternalVariable(variable,
+ new XdmAtomicValue((Long) value));
+ } else if (value instanceof String) {
+ evaluator.setExternalVariable(variable,
+ new XdmAtomicValue((String) value));
+ } else if (value instanceof URI) {
+ evaluator.setExternalVariable(variable,
+ new XdmAtomicValue((URI) value));
+ } else {
+ throw new BadInjectedFieldException(
+ "atomicInputVariables", getTaskName(),
+ "'" + atomicInputVariable + "' type is not supported: "
+ + value.getClass());
+ }
+ }
+
+ // Evaluate the query and collect the output.
+ StringBuilder output = new StringBuilder();
+ Iterator<XdmItem> xdmItems = evaluator.iterator();
+ while (xdmItems.hasNext()) {
+ XdmItem item = xdmItems.next();
+
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("XQuery result item = " + item);
+ }
+
+ output.append(item.toString());
+ }
+
+ // Set the output variable.
+ execution.setVariable(theOutputVariable, output.toString());
+
+ if (msoLogger.isDebugEnabled()) {
+ msoLogger.debug("Done Executing " + getTaskName());
+ }
+ }
+
+ /**
+ * Compiles an XQuery script contained in a resource (file).
+ * @param compiler the XQueryCompiler
+ * @param resource the resource path
+ * @return an XQueryExecutable
+ * @throws Exception on error
+ */
+ private XQueryExecutable compile(XQueryCompiler compiler, String resource)
+ throws Exception {
+ InputStream xqStream = null;
+ try {
+ xqStream = getClass().getResourceAsStream(resource);
+
+ if (xqStream == null) {
+ throw new IOException("Resource not found: " + resource);
+ }
+
+ XQueryExecutable executable = compiler.compile(xqStream);
+ xqStream.close();
+ xqStream = null;
+ return executable;
+ } finally {
+ if (xqStream != null) {
+ try {
+ xqStream.close();
+ } catch (Exception e) {
+ // Do nothing
+ }
+ }
+ }
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/json/JsonUtils.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/json/JsonUtils.java
new file mode 100644
index 0000000000..8329746347
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/json/JsonUtils.java
@@ -0,0 +1,443 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core.json;
+
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.XML;
+
+//import org.openecomp.mso.bpmn.core.BPMNLogger;
+import org.openecomp.mso.bpmn.core.xml.XmlTool;
+import org.openecomp.mso.logger.MsoLogger;
+
+/**
+ * Utility class for JSON processing
+ *
+ * @version 1.0
+ */
+
+public class JsonUtils {
+
+ private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+ private static int MSOJsonIndentFactor = 3;
+
+ /**
+ * Uses the JSONObject static method to convert a XML doc to JSON.
+ *
+ * @param xml String containing the XML doc
+ * @param pretty flag to determine if the output should be formatted
+ * @return String containing the JSON translation
+ */
+ public static String xml2json(String xml, Boolean pretty) {
+// String isDebugLogEnabled = "true";
+ try {
+ // name spaces cause problems, so just remove them
+ JSONObject jsonObj = XML.toJSONObject(XmlTool.removeNamespaces(xml));
+ if (!pretty) {
+ return jsonObj.toString();
+ } else {
+ // add an indent to make it 'pretty'
+ return jsonObj.toString(MSOJsonIndentFactor);
+ }
+ } catch (Exception e){
+ msoLogger.debug("xml2json(): unable to parse xml and convert to json. Exception was: " + e.toString());
+ return null;
+ }
+ }
+
+ /**
+ * Invokes xml2json(String, Boolean) defaulting to 'pretty' output.
+ *
+ * @param xml String containing the XML doc
+ * @return String containing the JSON translation
+ */
+ public static String xml2json(String xml) {
+ return xml2json(xml, true);
+ }
+
+ /**
+ * Uses the JSONObject static method to convert a JSON doc to XML.
+ * Note: this method will not generate valid XML if the JSONObject
+ * contains JSONArrays which are used to represent XML attributes
+ * in the JSON doc.
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param pretty flag to determine if the output should be formatted
+ * @return String containing the XML translation
+ */
+ public static String json2xml(String jsonStr, Boolean pretty) {
+// String isDebugLogEnabled = "true";
+ try {
+ JSONObject jsonObj = new JSONObject(jsonStr);
+ if (pretty) {
+ return XmlTool.normalize(XML.toString(jsonObj));
+ } else {
+ return XML.toString(jsonObj);
+ }
+ } catch (Exception e){
+ msoLogger.debug("json2xml(): unable to parse json and convert to xml. Exception was: " + e.toString());
+ return null;
+ }
+ }
+
+ /**
+ * Invokes json2xml(String, Boolean) defaulting to 'pretty' output.
+ *
+ * @param jsonStr String containing the XML doc
+ * @return String containing the JSON translation
+ */
+ public static String json2xml(String jsonStr) {
+ return json2xml(jsonStr, true);
+ }
+
+ /**
+ * Uses the JSONObject static method to convert a JSON doc to XML.
+ *
+ * @param jsonStr String containing the JSON doc
+ * @return Iterator over the JSON keys
+ */
+ public static Iterator <String> getJsonIterator(String jsonStr) {
+// String isDebugLogEnabled = "true";
+ try {
+ JSONObject json = new JSONObject(jsonStr);
+ return json.keys();
+
+ } catch (Exception e){
+ msoLogger.debug("getJsonIterator(): unable to parse json to retrieve the keys iterator. Exception was: " + e.toString());
+ return null;
+ }
+ }
+
+ /**
+ * Invokes the getJsonRawValue() method and returns the String equivalent of
+ * the object returned.
+ *
+ * TBD: May need separate methods for boolean, float, and integer fields if the
+ * String representation is not sufficient to meet client needs.
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param keys full key path to the target value in the format of "key1.key2.key3..."
+ * @return String field value associated with keys
+ */
+ public static String getJsonValue(String jsonStr, String keys) {
+// String isDebugLogEnabled = "true";
+ try {
+ Object rawValue = getJsonRawValue(jsonStr, keys);
+ if (rawValue == null) {
+ return null;
+ } else {
+ if (rawValue instanceof String) {
+ msoLogger.debug("getJsonValue(): the raw value is a String Object=" + ((String) rawValue).toString());
+ return (String) rawValue;
+ } else {
+ msoLogger.debug("getJsonValue(): the raw value is NOT a String Object=" + rawValue.toString());
+ return rawValue.toString();
+ }
+ }
+ } catch (Exception e) {
+ msoLogger.debug("getJsonValue(): unable to parse json to retrieve value for field=" + keys + ". Exception was: " + e.toString());
+ }
+ return null;
+ }
+
+ /**
+ * Invokes the getJsonRawValue() method to obtain the JSONArray associated with
+ * the specified keys. The JSONArray is then walked to retrieve the content value of
+ * the specified field name.
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param keys full key path to the target value in the format of "key1.key2.key3..."
+ * @param name field name for the param to be retrieved
+ * @return String param value associated with field name
+ */
+ public static String getJsonParamValue(String jsonStr, String keys, String name) {
+// String isDebugLogEnabled = "true";
+ try {
+ Object rawValue = getJsonRawValue(jsonStr, keys);
+ if (rawValue == null) {
+ return null;
+ } else {
+ if (rawValue instanceof JSONArray) {
+ msoLogger.debug("getJsonParamValue(): keys=" + keys + " points to JSONArray: " + ((JSONArray) rawValue).toString());
+ for (int i = 0; i < ((JSONArray) rawValue).length(); i++) {
+ msoLogger.debug("getJsonParamValue(): index: " + i + ", value: " + ((JSONArray) rawValue).get(i).toString());
+ if (((JSONArray) rawValue).get(i) instanceof JSONObject) {
+ msoLogger.debug("getJsonParamValue(): index: " + i + " is a JSONObject");
+ JSONObject jsonObj = (JSONObject)((JSONArray) rawValue).get(i);
+ if (jsonObj.get("name").equals(name)) {
+ msoLogger.debug("getJsonParamValue(): found value: " + (String) jsonObj.get("content") + " for name: " + name);
+ return (String) jsonObj.get("content");
+ }
+ } else {
+ msoLogger.debug("getJsonParamValue(): the JSONArray element is NOT a JSONObject=" + rawValue.toString());
+ return null;
+ }
+ }
+ msoLogger.debug("getJsonParamValue(): content value NOT found for name: " + name);
+ return null;
+ } else {
+ msoLogger.debug("getJsonParamValue(): the raw value is NOT a JSONArray Object=" + rawValue.toString());
+ return null;
+ }
+ }
+ } catch (JSONException je) {
+ // JSONObject::get() throws this exception if one of the specified keys is not found
+ msoLogger.debug("getJsonParamValue(): caught JSONException attempting to retrieve param value for keys:" + keys + ", name=" + name);
+ } catch (Exception e) {
+ msoLogger.debug("getJsonParamValue(): unable to parse json to retrieve value for field=" + keys + ". Exception was: " + e.toString());
+ }
+ return null;
+ }
+
+ /**
+ * Wrapper to generate the JSONObject to pass to the getJsonValueForKey(JSONObject, String)
+ * method so that recursion over the subobjects can be supported there
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param key key to the target value
+ * @return String field value associated with key
+ */
+ public static String getJsonValueForKey(String jsonStr, String key) {
+// String isDebugLogEnabled = "true";
+ try {
+ JSONObject jsonObj = new JSONObject(jsonStr);
+ if (jsonObj != null) {
+ return getJsonValueForKey(jsonObj, key);
+ }
+ } catch (Exception e) {
+ msoLogger.debug("getJsonValueForKey(): unable to parse json to retrieve value for field=" + key + ". Exception was: " + e.toString());
+ }
+ return null;
+ }
+
+ /**
+ * Walks the JSONObject (and sub-objects recursively), searching for the first value associated with the
+ * single key/field name specified. Returns the associated value if found or null if the key is not found
+ *
+ * @param jsonObj JSONObject representation of the the JSON doc
+ * @param key key to the target value
+ * @return String field value associated with key
+ */
+ public static String getJsonValueForKey(JSONObject jsonObj, String key) {
+// String isDebugLogEnabled = "true";
+ String keyValue = null;
+ try {
+ if (jsonObj.has(key)) {
+ msoLogger.debug("getJsonValueForKey(): found value for key=" + key);
+ return ((String) jsonObj.get(key));
+ } else {
+ msoLogger.debug("getJsonValueForKey(): iterating over the keys");
+ Iterator <String> itr = jsonObj.keys();
+ while (itr.hasNext()) {
+ String nextKey = (String) itr.next();
+ Object obj = jsonObj.get(nextKey);
+ if (obj instanceof JSONObject) {
+ msoLogger.debug("getJsonValueForKey(): key=" + nextKey + ", points to JSONObject, recursive call");
+ keyValue = getJsonValueForKey((JSONObject) obj, key);
+ if (keyValue != null) {
+ msoLogger.debug("getJsonValueForKey(): found value=" + keyValue + ", for key=" + key);
+ break;
+ }
+ } else {
+ msoLogger.debug("getJsonValueForKey(): key=" + nextKey + ", does not point to a JSONObject, next key");
+ }
+ }
+ }
+ } catch (JSONException je) {
+ // JSONObject::get() throws this exception if one of the specified keys is not found
+ msoLogger.debug("getJsonValueForKey(): caught JSONException attempting to retrieve value for key=" + key);
+ keyValue = null;
+ } catch (Exception e) {
+ msoLogger.debug("getJsonValueForKey(): unable to parse json to retrieve value for field=" + key + ". Exception was: " + e.toString());
+ }
+ return keyValue;
+ }
+
+ /**
+ * Boolean method to determine if a key path is valid for the JSON doc. Invokes
+ * getJsonValue().
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param keys full key path to the target value in the format of "key1.key2.key3..."
+ * @return Boolean true if keys points to value in the JSON doc
+ */
+ public static Boolean jsonValueExists(String jsonStr, String keys) {
+ if (getJsonRawValue(jsonStr, keys) == null) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Inserts the new key/value pair at the appropriate location in the JSON
+ * document after first determining if keyed field already exists. If
+ * it does exist, return the JSON unmodified, otherwise return the new JSON
+ * Note: this method currently only supports String value inserts.
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param keys full key path to the value to be added in the format of "key1.key2.key3..."
+ * @return String containing the updated JSON doc
+ */
+ public static String addJsonValue(String jsonStr, String keys, String value) {
+// String isDebugLogEnabled = "true";
+ // only attempt to insert the key/value pair if it does not exist
+ if (!jsonValueExists(jsonStr, keys)) {
+ return putJsonValue(jsonStr, keys, value);
+ } else {
+ msoLogger.debug("addJsonValue(): JSON add failed, key=" + keys + "/value=" + (String) value + " already exists");
+ return jsonStr;
+ }
+ }
+
+ /**
+ * Updates the value for the specified key in the JSON document
+ * after first determining if keyed field exists. If it does
+ * not exist, return the JSON unmodified, otherwise return the updated JSON.
+ * Note: this method currently only supports String value updates.
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param keys full key path to the value to be updated in the format of "key1.key2.key3..."
+ * @return String containing the updated JSON doc
+ */
+ public static String updJsonValue(String jsonStr, String keys, String newValue) {
+// String isDebugLogEnabled = "true";
+ // only attempt to modify the key/value pair if it exists
+ if (jsonValueExists(jsonStr, keys)) {
+ return putJsonValue(jsonStr, keys, newValue);
+ } else {
+ msoLogger.debug("updJsonValue(): JSON update failed, no value exists for key=" + keys);
+ return jsonStr;
+ }
+ }
+
+ /**
+ * Deletes the value for the specified key in the JSON document
+ * after first determining if keyed field exists. If it does
+ * not exist, return the JSON unmodified, otherwise return the updated JSON
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param keys full key path to the value to be deleted in the format of "key1.key2.key3..."
+ * @return String containing the updated JSON doc
+ */
+ public static String delJsonValue(String jsonStr, String keys) {
+// String isDebugLogEnabled = "true";
+ // only attempt to remove the key/value pair if it exists
+ if (jsonValueExists(jsonStr, keys)) {
+ // passing a null value results in a delete
+ return putJsonValue(jsonStr, keys, null);
+ } else {
+ msoLogger.debug("delJsonValue(): JSON delete failed, no value exists for key=" + keys);
+ return jsonStr;
+ }
+ }
+
+ /**
+ * Walks the JSON doc using the full key path to retrieve the associated
+ * value. All but the last key points to the 'parent' object name(s) in order
+ * in the JSON hierarchy with the last key pointing to the target value.
+ * The value returned is a Java object.
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param keys full key path to the target value in the format of "key1.key2.key3..."
+ * @return Object field value associated with keys
+ */
+ private static Object getJsonRawValue(String jsonStr, String keys) {
+// String isDebugLogEnabled = "true";
+ String keyStr = "";
+ try {
+ JSONObject jsonObj = new JSONObject(jsonStr);
+ StringTokenizer keyTokens = new StringTokenizer(keys, ".");
+ while (keyTokens.hasMoreElements()) {
+ keyStr = keyTokens.nextToken();
+ Object keyValue = jsonObj.get(keyStr);
+ if (keyValue instanceof JSONObject) {
+ msoLogger.debug("getJsonRawValue(): key=" + keyStr + " points to json object");
+ jsonObj = (JSONObject) keyValue;
+ } else {
+ if (keyTokens.hasMoreElements()) {
+ msoLogger.debug("getJsonRawValue(): value found prior to last key for key=" + keyStr);
+ }
+ return keyValue;
+ }
+ }
+ // we should not hit this point: either the key points to a valid value and
+ // we return it above or the key is invalid and we handle the JSONException
+ // below and return null
+ return null;
+
+ } catch (JSONException je) {
+ // JSONObject::get() throws this exception if one of the specified keys is not found
+ msoLogger.debug("getJsonRawValue(): caught JSONException attempting to retrieve raw value for key=" + keyStr);
+ } catch (Exception e) {
+ msoLogger.debug("getJsonRawValue(): unable to parse json to retrieve value for field=" + keys + ". Exception was: " + e.toString());
+ }
+ return null;
+ }
+
+ /**
+ * Private method invoked by the public add, update, and delete methods.
+ *
+ * @param jsonStr String containing the JSON doc
+ * @param keys full key path to the value to be deleted in the format of "key1.key2.key3..."
+ * @return String containing the updated JSON doc
+ */
+ private static String putJsonValue(String jsonStr, String keys, String value) {
+// String isDebugLogEnabled = "true";
+ String keyStr = "";
+ try {
+ JSONObject jsonObj = new JSONObject(jsonStr);
+ JSONObject jsonObjOut = jsonObj;
+ StringTokenizer keyTokens = new StringTokenizer(keys, ".");
+ while (keyTokens.hasMoreElements()) {
+ keyStr = keyTokens.nextToken();
+ if (keyTokens.hasMoreElements()) {
+ Object keyValue = jsonObj.get(keyStr);
+ if (keyValue instanceof JSONObject) {
+ msoLogger.debug("putJsonValue(): key=" + keyStr + " points to json object");
+ jsonObj = (JSONObject) keyValue;
+ } else {
+ msoLogger.debug("putJsonValue(): key=" + keyStr + " not the last key but points to non-json object: " + (String) keyValue);
+ return null;
+ }
+ } else { // at the last/new key value
+ jsonObj.put(keyStr, value);
+ return jsonObjOut.toString(3);
+ }
+ }
+ // should not hit this point if the key points to a valid key value
+ return null;
+
+ } catch (JSONException je) {
+ // JSONObject::get() throws this exception if one of the specified keys is not found
+ msoLogger.debug("putJsonValue(): caught JSONException attempting to retrieve value for key=" + keyStr);
+ return null;
+ } catch (Exception e) {
+ msoLogger.debug("putJsonValue(): unable to parse json to put value for key=" + keys + ". Exception was: " + e.toString());
+ }
+ return null;
+ }
+}
+
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/mybatis/CustomMyBatisSessionFactory.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/mybatis/CustomMyBatisSessionFactory.java
new file mode 100644
index 0000000000..3a731558a4
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/mybatis/CustomMyBatisSessionFactory.java
@@ -0,0 +1,102 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core.mybatis;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.camunda.bpm.engine.impl.cfg.StandaloneProcessEngineConfiguration;
+import org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor;
+import org.camunda.bpm.engine.impl.interceptor.CommandInterceptor;
+import org.camunda.bpm.engine.impl.interceptor.LogInterceptor;
+import org.camunda.bpm.engine.impl.util.ReflectUtil;
+
+
+/**
+ * A special process engine that provides access to MyBatis mappings.
+ * @version 1.0
+ */
+public class CustomMyBatisSessionFactory extends
+ StandaloneProcessEngineConfiguration {
+
+ private String resourceName;
+
+ /**
+ * Overridden to ensure nobody ever tries to initialize this process engine
+ * in the normal way. We are using this process engine only for MyBatis
+ * access.
+ */
+ @Override
+ protected void init() {
+ throw new UnsupportedOperationException("init");
+ }
+
+ /**
+ * Initialize the ProcessEngineConfiguration from an existing one, just
+ * using the database settings to initialize the database / MyBatis stuff.
+ */
+ public void initFromProcessEngineConfiguration(
+ ProcessEngineConfigurationImpl processEngineConfiguration,
+ String resourceName) {
+ this.resourceName = resourceName;
+
+ setDatabaseType(processEngineConfiguration.getDatabaseType());
+ setDataSource(processEngineConfiguration.getDataSource());
+ setDatabaseTablePrefix(processEngineConfiguration
+ .getDatabaseTablePrefix());
+
+ initDataSource();
+ // initVariableTypes();
+ initCommandContextFactory();
+ initTransactionFactory();
+ initTransactionContextFactory();
+ initCommandExecutors();
+ initSqlSessionFactory();
+ initIncidentHandlers();
+ initIdentityProviderSessionFactory();
+ initSessionFactories();
+ }
+
+ /**
+ * In order to always open a new command context set the property
+ * "alwaysOpenNew" to true inside the CommandContextInterceptor.
+ *
+ * If you execute the custom queries inside the process engine (for example
+ * in a service task), you have to do this.
+ */
+ @Override
+ protected Collection<? extends CommandInterceptor> getDefaultCommandInterceptorsTxRequired() {
+ List<CommandInterceptor> defaultCommandInterceptorsTxRequired =
+ new ArrayList<CommandInterceptor>();
+ defaultCommandInterceptorsTxRequired.add(new LogInterceptor());
+ defaultCommandInterceptorsTxRequired.add(new CommandContextInterceptor(
+ commandContextFactory, this, true));
+ return defaultCommandInterceptorsTxRequired;
+ }
+
+ @Override
+ protected InputStream getMyBatisXmlConfigurationSteam() {
+ return ReflectUtil.getResourceAsStream(resourceName);
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/mybatis/URNMapping.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/mybatis/URNMapping.java
new file mode 100644
index 0000000000..07959cb016
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/mybatis/URNMapping.java
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core.mybatis;
+
+/**
+ * A bean that represents a single URN mapping.
+ */
+public class URNMapping {
+ private String name;
+ private String value;
+ private String rev;
+
+ /**
+ * Get the name.
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name.
+ * @param name the name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the value mapped to the name.
+ * @return the value mapped to the name
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * Set the value mapped to the name.
+ * @param value the value mapped to the name
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the revision attribute (currently unused).
+ * @return the revision attribute
+ */
+ public String getRev() {
+ return rev;
+ }
+
+ /**
+ * Set the revision attribute (currently unused).
+ * @param rev the revision attribute
+ */
+ public void setRev(String rev) {
+ this.rev = rev;
+ }
+
+ /**
+ * Converts a URN to "normal" form so it can used as a java or groovy
+ * variable identifier. This is done in a way that makes the identifier
+ * as readable as possible, but note that it might result in a loss of
+ * uniqueness.
+ * <ol>
+ * <li> URN_ is prepended </li>
+ * <li> All characters that are not letters or digits are converted to
+ * underscore characters </li>
+ * <li> Sequences of multiple underscores are collapsed to a single
+ * underscore character </li>
+ * </ol>
+ * Examples:
+ * <p>
+ * aai:endpoint becomes URN_aai_endpoint <br/>
+ * ae:internal-reporting becomes URN_ae_internal_reporting <br/>
+ *
+ * @param name the URN
+ * @return a normalized identifier
+ */
+ public static String createIdentifierFromURN(String urn) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("URN_");
+ char last = builder.charAt(builder.length() - 1);
+
+ int len = urn.length();
+
+ for (int i = 0; i < len; i++) {
+ char c = urn.charAt(i);
+
+ if (!Character.isLetterOrDigit(c) && c != '_') {
+ c = '_';
+ }
+
+ if (!(c == '_' && last == '_')) {
+ builder.append(c);
+ }
+
+ last = c;
+ }
+
+ return builder.toString();
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/plugins/LoggingAndURNMappingPlugin.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/plugins/LoggingAndURNMappingPlugin.java
new file mode 100644
index 0000000000..8e3f254def
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/plugins/LoggingAndURNMappingPlugin.java
@@ -0,0 +1,424 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core.plugins;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+import org.camunda.bpm.engine.delegate.ExecutionListener;
+import org.camunda.bpm.engine.impl.bpmn.parser.AbstractBpmnParseListener;
+import org.camunda.bpm.engine.impl.bpmn.parser.BpmnParseListener;
+import org.camunda.bpm.engine.impl.cfg.AbstractProcessEnginePlugin;
+import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.camunda.bpm.engine.impl.context.Context;
+import org.camunda.bpm.engine.impl.interceptor.Command;
+import org.camunda.bpm.engine.impl.interceptor.CommandContext;
+import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity;
+import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
+import org.camunda.bpm.engine.impl.pvm.process.ScopeImpl;
+import org.camunda.bpm.engine.impl.pvm.process.TransitionImpl;
+import org.camunda.bpm.engine.impl.util.xml.Element;
+import org.camunda.bpm.engine.impl.variable.VariableDeclaration;
+
+import org.openecomp.mso.bpmn.core.BPMNLogger;
+import org.openecomp.mso.bpmn.core.PropertyConfiguration;
+import org.openecomp.mso.bpmn.core.mybatis.CustomMyBatisSessionFactory;
+import org.openecomp.mso.bpmn.core.mybatis.URNMapping;
+import org.openecomp.mso.logger.MessageEnum;
+import org.openecomp.mso.logger.MsoLogger;
+
+/**
+ * Plugin for MSO logging and URN mapping.
+ */
+public class LoggingAndURNMappingPlugin extends AbstractProcessEnginePlugin {
+ private static MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+ private static final String FSPROPKEY = "URNMapping.FileSystemLoading.Enabled";
+
+ @Override
+ public void preInit(
+ ProcessEngineConfigurationImpl processEngineConfiguration) {
+ List<BpmnParseListener> preParseListeners = processEngineConfiguration
+ .getCustomPreBPMNParseListeners();
+ if (preParseListeners == null) {
+ preParseListeners = new ArrayList<BpmnParseListener>();
+ processEngineConfiguration.setCustomPreBPMNParseListeners(preParseListeners);
+ }
+ preParseListeners.add(new LoggingParseListener());
+ }
+
+ /**
+ * Called when a process flow is parsed so we can inject listeners.
+ */
+ public static class LoggingParseListener extends AbstractBpmnParseListener {
+ private void injectLogExecutionListener(ActivityImpl activity) {
+ activity.addListener(
+ ExecutionListener.EVENTNAME_END,
+ new LoggingExecutionListener("END"));
+
+ activity.addListener(
+ ExecutionListener.EVENTNAME_START,
+ new LoggingExecutionListener("START"));
+
+ activity.addListener(
+ ExecutionListener.EVENTNAME_TAKE,
+ new LoggingExecutionListener("TAKE"));
+ }
+
+ public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition) {
+ }
+
+ public void parseStartEvent(Element startEventElement, ScopeImpl scope, ActivityImpl startEventActivity) {
+ // Inject these listeners only on the main start event for the flow, not on any embedded subflow start events
+ if (scope instanceof ProcessDefinitionEntity) {
+ startEventActivity.addListener(ExecutionListener.EVENTNAME_START, new URNMappingInitializerListener("START"));
+ startEventActivity.addListener(ExecutionListener.EVENTNAME_START, new LoggingInitializerListener("START"));
+ }
+
+ injectLogExecutionListener(startEventActivity);
+ }
+
+ public void parseServiceTask(Element serviceTaskElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseExclusiveGateway(Element exclusiveGwElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseInclusiveGateway(Element inclusiveGwElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseParallelGateway(Element parallelGwElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseScriptTask(Element scriptTaskElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseBusinessRuleTask(Element businessRuleTaskElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseTask(Element taskElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseManualTask(Element manualTaskElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseUserTask(Element userTaskElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseEndEvent(Element endEventElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseBoundaryTimerEventDefinition(Element timerEventDefinition, boolean interrupting, ActivityImpl timerActivity) {
+ injectLogExecutionListener(timerActivity);
+ }
+
+ public void parseBoundaryErrorEventDefinition(Element errorEventDefinition, boolean interrupting, ActivityImpl activity, ActivityImpl nestedErrorEventActivity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseSubProcess(Element subProcessElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseCallActivity(Element callActivityElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseProperty(Element propertyElement, VariableDeclaration variableDeclaration, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseSequenceFlow(Element sequenceFlowElement, ScopeImpl scopeElement, TransitionImpl transition) {
+ //injectLogExecutionListener(activity);
+ }
+
+ public void parseSendTask(Element sendTaskElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseMultiInstanceLoopCharacteristics(Element activityElement, Element multiInstanceLoopCharacteristicsElement, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseIntermediateTimerEventDefinition(Element timerEventDefinition, ActivityImpl timerActivity) {
+ injectLogExecutionListener(timerActivity);
+ }
+
+ public void parseRootElement(Element rootElement, List<ProcessDefinitionEntity> processDefinitions) {
+ //injectLogExecutionListener(activity);
+ }
+
+ public void parseReceiveTask(Element receiveTaskElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseIntermediateSignalCatchEventDefinition(Element signalEventDefinition, ActivityImpl signalActivity) {
+ injectLogExecutionListener(signalActivity);
+ }
+
+ public void parseBoundarySignalEventDefinition(Element signalEventDefinition, boolean interrupting, ActivityImpl signalActivity) {
+ injectLogExecutionListener(signalActivity);
+ }
+
+ public void parseEventBasedGateway(Element eventBasedGwElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseTransaction(Element transactionElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseCompensateEventDefinition(Element compensateEventDefinition, ActivityImpl compensationActivity) {
+ injectLogExecutionListener(compensationActivity);
+ }
+
+ public void parseIntermediateThrowEvent(Element intermediateEventElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseIntermediateCatchEvent(Element intermediateEventElement, ScopeImpl scope, ActivityImpl activity) {
+ injectLogExecutionListener(activity);
+ }
+
+ public void parseBoundaryEvent(Element boundaryEventElement, ScopeImpl scopeElement, ActivityImpl nestedActivity) {
+ injectLogExecutionListener(nestedActivity);
+ }
+
+ public void parseIntermediateMessageCatchEventDefinition(Element messageEventDefinition, ActivityImpl nestedActivity) {
+ injectLogExecutionListener(nestedActivity);
+ }
+
+ public void parseBoundaryMessageEventDefinition(Element element, boolean interrupting, ActivityImpl messageActivity) {
+ injectLogExecutionListener(messageActivity);
+ }
+ }
+
+ /**
+ * Initializes URN mapping variables on process entry.
+ */
+ public static class URNMappingInitializerListener implements ExecutionListener {
+ private String event;
+
+ public URNMappingInitializerListener(String eventData) {
+ this.event = eventData;
+ }
+
+ public String getEvent() {
+ return event;
+ }
+
+ public void notify(DelegateExecution execution) throws Exception {
+ ProcessEngineConfigurationImpl processEngineConfiguration =
+ Context.getProcessEngineConfiguration();
+ loadURNProperties(execution, processEngineConfiguration);
+ }
+
+ private void loadURNProperties(DelegateExecution execution,
+ ProcessEngineConfigurationImpl processEngineConfiguration) {
+ Map<String,String> bpmnProps = PropertyConfiguration.getInstance().getProperties("mso.bpmn.properties");
+ if (bpmnProps == null) {
+ LOGGER.debug("Unable to load mso.bpmn.properties; loading URN Mapping from DB");
+
+ LOGGER.error (MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError,
+ "Unable to load mso.bpmn.properties; loading URN Mapping from DB");
+
+ loadFromDB(execution, processEngineConfiguration);
+ } else {
+ String fsEnabled = bpmnProps.get(FSPROPKEY);
+ if (fsEnabled != null) {
+ if (Boolean.parseBoolean(fsEnabled)) {
+ LOGGER.debug("File system loading is enabled; loading URN properties from File system");
+ LOGGER.info(MessageEnum.BPMN_GENERAL_INFO, "BPMN", "File system loading is enabled; loading URN properties from File System");
+ loadFromFileSystem(execution);
+ } else {
+ LOGGER.debug("File system loading is disabled; loading URN properties from DB");
+ LOGGER.info (MessageEnum.BPMN_GENERAL_INFO, "BPMN", "File system loading is disabled; loading URN properties from DB");
+
+ loadFromDB(execution, processEngineConfiguration);
+ }
+ } else {
+
+ LOGGER.error (MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError,
+ "Unable to retrieve URNMapping.FileSystemLoading.Enabled from mso.bpmn.properties; loading URN Mapping from DB");
+
+ loadFromDB(execution, processEngineConfiguration);
+ }
+ }
+ }
+
+ private void loadFromFileSystem(DelegateExecution execution) {
+ PropertyConfiguration propertyConfiguration = PropertyConfiguration.getInstance();
+ Map<String,String> props = propertyConfiguration.getProperties("mso.bpmn.urn.properties");
+ for (String key : props.keySet()) {
+ String varName = URNMapping.createIdentifierFromURN(key);
+ String varValue = props.get(key);
+ execution.setVariable(varName, varValue);
+ }
+ }
+
+ private void loadFromDB(DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {
+ Command<List<URNMapping>> command = new Command<List<URNMapping>>() {
+ @SuppressWarnings("unchecked")
+ public List<URNMapping> execute(CommandContext commandContext) {
+ return (List<URNMapping>) commandContext.getDbSqlSession().selectList(
+ "mso.urnMapping.selectAll", null);
+ }
+ };
+
+ CustomMyBatisSessionFactory sessionFactory = new CustomMyBatisSessionFactory();
+ sessionFactory.initFromProcessEngineConfiguration(processEngineConfiguration,
+ "customMyBatisConfiguration.xml");
+
+ List<URNMapping> mappings = sessionFactory.getCommandExecutorTxRequired().execute(command);
+
+ if (mappings != null && mappings.size() > 0) {
+ for (URNMapping mapping : mappings) {
+ String varName = URNMapping.createIdentifierFromURN(mapping.getName());
+ String varValue = mapping.getValue();
+
+ LOGGER.debug("URN Mapping = '" + mapping.getName()
+ + "', setting variable '" + varName + "' to '" + varValue + "'");
+
+ execution.setVariable(varName, varValue);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the isDebugLogEnabled variable on process entry.
+ */
+ public static class LoggingInitializerListener implements ExecutionListener {
+ private String event;
+
+ public LoggingInitializerListener(String eventData) {
+ this.event = eventData;
+ }
+
+ public String getEvent() {
+ return event;
+ }
+
+ public void notify(DelegateExecution execution) throws Exception {
+ String processKey = execution.getProcessEngineServices().getRepositoryService()
+ .getProcessDefinition(execution.getProcessDefinitionId()).getKey();
+
+ // If a "true" value is already injected, e.g. from a top-level flow, it SHOULD NOT be
+ // overridden by the value in the URN mapping. This allows a top-level flow and all
+ // invoked subflows to be debugged by turning on the debug flag for just the top-level
+ // flow, assuming the isDebugEnabled flag variable is passed from the top-level flow to
+ // its subflows.
+
+ // If a "false" value is already injected, e.g. from a top-level flow, it SHOULD be
+ // overridden by the value in the URN mapping. This allows a subflow to be debugged
+ // without turning on the the debug flag for the top-level flow.
+
+ String injectedValue = (String) execution.getVariable("isDebugLogEnabled");
+ String urnValue = "true".equals(execution.getVariable("URN_log_debug_" + processKey)) ? "true" : "false";
+
+ if ("true".equals(injectedValue)) {
+ LOGGER.debug("Setting isDebugLogEnabled to \"" + injectedValue + "\" for process: " + processKey + " (injected value)");
+ execution.setVariable("isDebugLogEnabled", injectedValue);
+ } else {
+ LOGGER.debug("Setting isDebugLogEnabled to \"" + urnValue + "\" for process: " + processKey + " (from URN mapping)");
+ execution.setVariable("isDebugLogEnabled", urnValue);
+ }
+ }
+ }
+
+ /**
+ * Logs details about the current activity.
+ */
+ public static class LoggingExecutionListener implements ExecutionListener {
+ private static MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
+ private static ConcurrentHashMap<String, Long> startTimes = new ConcurrentHashMap<String, Long>();
+
+ private String event;
+
+ public LoggingExecutionListener(String event) {
+ this.event = event;
+ }
+
+ public String getEvent() {
+ return event;
+ }
+
+ public void notify(DelegateExecution execution) throws Exception {
+ BPMNLogger.debug(
+ (String) execution.getVariable("isDebugLogEnabled"),
+ "Logging for activity---------------:" + event + ":"
+ + execution.getCurrentActivityName()
+ + ", processDefinitionId="
+ + execution.getProcessDefinitionId() + ", activtyId="
+ + execution.getCurrentActivityId() + ", activtyName='"
+ + execution.getCurrentActivityName() + "'"
+ + ", processInstanceId="
+ + execution.getProcessInstanceId() + ", businessKey="
+ + execution.getProcessBusinessKey() + ", executionId="
+ + execution.getId());
+
+ if (!isBlank(execution.getCurrentActivityName())) {
+ try {
+ String id = execution.getId();
+ if ("START".equals(event) && id != null ) {
+ startTimes.put(id, (Long)System.currentTimeMillis());
+ } else if ("END".equals(event) && id != null) {
+ String prefix = (String) execution.getVariable("prefix");
+
+ if (prefix != null ) {
+ MsoLogger.setServiceName("MSO." + prefix.substring(0,prefix.length()-1));
+ }
+
+ String requestId = (String) execution.getVariable("att-mso-request-id");
+ String svcid = (String) execution.getVariable("att-mso-service-instance-id");
+ MsoLogger.setLogContext(requestId, svcid);
+ long startTime = startTimes.remove(id);
+
+ if (startTime != 0) {
+
+ LOGGER.recordMetricEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc,
+ event + ": " + execution.getCurrentActivityName(), "BPMN", execution.getCurrentActivityName(), null);
+
+ }
+ }
+ } catch(Exception e) {
+ // Do nothing
+ }
+ }
+ }
+
+ private boolean isBlank(Object object) {
+ return object == null || object.toString().trim().equals("");
+ }
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/plugins/WorkflowExceptionPlugin.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/plugins/WorkflowExceptionPlugin.java
new file mode 100644
index 0000000000..10386d721e
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/plugins/WorkflowExceptionPlugin.java
@@ -0,0 +1,170 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core.plugins;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.camunda.bpm.engine.delegate.BpmnError;
+import org.camunda.bpm.engine.delegate.DelegateExecution;
+import org.camunda.bpm.engine.delegate.ExecutionListener;
+import org.camunda.bpm.engine.delegate.JavaDelegate;
+import org.camunda.bpm.engine.impl.bpmn.behavior.ClassDelegateActivityBehavior;
+import org.camunda.bpm.engine.impl.bpmn.parser.AbstractBpmnParseListener;
+import org.camunda.bpm.engine.impl.bpmn.parser.BpmnParseListener;
+import org.camunda.bpm.engine.impl.bpmn.parser.FieldDeclaration;
+import org.camunda.bpm.engine.impl.cfg.AbstractProcessEnginePlugin;
+import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity;
+import org.camunda.bpm.engine.impl.pvm.PvmTransition;
+import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
+import org.camunda.bpm.engine.impl.pvm.process.TransitionImpl;
+import org.camunda.bpm.engine.impl.util.xml.Element;
+
+import org.openecomp.mso.bpmn.core.BPMNLogger;
+import org.openecomp.mso.bpmn.core.WorkflowException;
+
+/**
+ * This plugin does the following:
+ * <ol>
+ * <li>
+ * Adds logic at the start of every Call Activity to remove any existing
+ * WorkflowException object from the execution (saving a copy of it in a
+ * different variable).
+ * </li>
+ * <li>
+ * Adds logic at the end of every Call Activity to generate a MSOWorkflowException
+ * event if there is a WorkflowException object in the execution.
+ * </li>
+ * </ol>
+ */
+public class WorkflowExceptionPlugin extends AbstractProcessEnginePlugin {
+
+ @Override
+ public void preInit(ProcessEngineConfigurationImpl processEngineConfiguration) {
+ List<BpmnParseListener> preParseListeners =
+ processEngineConfiguration.getCustomPreBPMNParseListeners();
+
+ if (preParseListeners == null) {
+ preParseListeners = new ArrayList<BpmnParseListener>();
+ processEngineConfiguration.setCustomPreBPMNParseListeners(preParseListeners);
+ }
+
+ preParseListeners.add(new WorkflowExceptionParseListener());
+ }
+
+ public static class WorkflowExceptionParseListener extends AbstractBpmnParseListener {
+ @Override
+ public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition) {
+ AtomicInteger triggerTaskIndex = new AtomicInteger(1);
+ List<ActivityImpl> activities = new ArrayList<ActivityImpl>(processDefinition.getActivities());
+ recurse(activities, triggerTaskIndex);
+ }
+
+ /**
+ * Helper method that recurses (into subprocesses) over all the listed activities.
+ * @param activities a list of workflow activities
+ * @param triggerTaskIndex the index of the next trigger task (mutable)
+ */
+ private void recurse(List<ActivityImpl> activities, AtomicInteger triggerTaskIndex) {
+ for (ActivityImpl activity : activities) {
+ String type = (String) activity.getProperty("type");
+
+ if ("callActivity".equals(type)) {
+ // Add a WorkflowExceptionResetListener to clear the WorkflowException
+ // variable when each Call Activity starts.
+
+ activity.addListener(
+ ExecutionListener.EVENTNAME_START,
+ new WorkflowExceptionResetListener());
+
+ // Add a WorkflowExceptionTriggerTask after the call activity.
+ // It must be a task because a listener cannot be used to generate
+ // an event. Throwing BpmnError from an execution listener will
+ // cause the process to die.
+
+ List<PvmTransition> outTransitions =
+ new ArrayList<PvmTransition>(activity.getOutgoingTransitions());
+
+ for (PvmTransition transition : outTransitions) {
+ String triggerTaskId = "WorkflowExceptionTriggerTask_" + triggerTaskIndex;
+
+ ActivityImpl triggerTask = activity.getFlowScope().createActivity(triggerTaskId);
+
+ ClassDelegateActivityBehavior behavior = new ClassDelegateActivityBehavior(
+ WorkflowExceptionTriggerTask.class.getName(),
+ new ArrayList<FieldDeclaration>(0));
+
+ triggerTask.setActivityBehavior(behavior);
+ triggerTask.setName("Workflow Exception Trigger Task " + triggerTaskIndex);
+ triggerTaskIndex.getAndIncrement();
+
+ TransitionImpl transitionImpl = (TransitionImpl) transition;
+ TransitionImpl triggerTaskOutTransition = triggerTask.createOutgoingTransition();
+ triggerTaskOutTransition.setDestination((ActivityImpl)transitionImpl.getDestination());
+ transitionImpl.setDestination(triggerTask);
+ }
+ } else if ("subProcess".equals(type)) {
+ recurse(new ArrayList<ActivityImpl>(activity.getActivities()), triggerTaskIndex);
+ }
+ }
+ }
+ }
+
+ /**
+ * If there is a WorkflowException object in the execution, this method
+ * removes it (saving a copy of it in a different variable).
+ */
+ public static class WorkflowExceptionResetListener implements ExecutionListener {
+ public void notify(DelegateExecution execution) throws Exception {
+ Object workflowException = execution.getVariable("WorkflowException");
+
+ if (workflowException instanceof WorkflowException) {
+ int index = 1;
+ String saveName = "SavedWorkflowException" + index;
+ while (execution.getVariable(saveName) != null) {
+ saveName = "SavedWorkflowException" + (++index);
+ }
+
+ BPMNLogger.debug((String)execution.getVariable("isDebugLogEnabled"),
+ "WorkflowExceptionResetTask is moving WorkflowException to " + saveName);
+
+ execution.setVariable(saveName, workflowException);
+ execution.setVariable("WorkflowException", null);
+ }
+ }
+ }
+
+ /**
+ * Generates an MSOWorkflowException event if there is a WorkflowException
+ * object in the execution.
+ */
+ public static class WorkflowExceptionTriggerTask implements JavaDelegate {
+ public void execute(DelegateExecution execution) throws Exception {
+ if (execution.getVariable("WorkflowException") instanceof WorkflowException) {
+ BPMNLogger.debug((String)execution.getVariable("isDebugLogEnabled"),
+ "WorkflowExceptionTriggerTask is generating a MSOWorkflowException event");
+ throw new BpmnError("MSOWorkflowException");
+ }
+ }
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/xml/XmlTool.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/xml/XmlTool.java
new file mode 100644
index 0000000000..42114758b0
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/core/xml/XmlTool.java
@@ -0,0 +1,340 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.core.xml;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * XML transformation methods and other useful functions.
+ */
+public final class XmlTool {
+
+ private static final Map<String, Integer> ENTITIES = new HashMap<String, Integer>();
+
+ static {
+ ENTITIES.put("amp", new Integer(38));
+ ENTITIES.put("quot", new Integer(34));
+ ENTITIES.put("lt", new Integer(60));
+ ENTITIES.put("gt", new Integer(62));
+ }
+
+ /**
+ * Normalizes and formats XML. This method consolidates and moves all namespace
+ * declarations to the root element. The result will not have an XML prolog or
+ * a trailing newline.
+ * @param xml the XML to normalize
+ * @throws IOException
+ * @throws TransformerException
+ * @throws ParserConfigurationException
+ * @throws SAXException
+ * @throws XPathExpressionException
+ */
+ public static String normalize(Object xml) throws IOException, TransformerException,
+ ParserConfigurationException, SAXException, XPathExpressionException {
+
+ if (xml == null) {
+ return null;
+ }
+
+ Source xsltSource = new StreamSource(new StringReader(
+ readResourceFile("normalize-namespaces.xsl")));
+
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ dbFactory.setNamespaceAware(true);
+ DocumentBuilder db = dbFactory.newDocumentBuilder();
+ InputSource source = new InputSource(new StringReader(String.valueOf(xml)));
+ Document doc = db.parse(source);
+
+ // Start of code to remove whitespace outside of tags
+ XPath xPath = XPathFactory.newInstance().newXPath();
+ NodeList nodeList = (NodeList) xPath.evaluate(
+ "//text()[normalize-space()='']", doc, XPathConstants.NODESET);
+
+ for (int i = 0; i < nodeList.getLength(); ++i) {
+ Node node = nodeList.item(i);
+ node.getParentNode().removeChild(node);
+ }
+ // End of code to remove whitespace outside of tags
+
+ // the factory pattern supports different XSLT processors
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer(xsltSource);
+
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+
+ StringWriter writer = new StringWriter();
+ transformer.transform(new DOMSource(doc), new StreamResult(writer));
+ return writer.toString().trim();
+ }
+
+ /**
+ * Encodes a value so it can be used inside an XML text element.
+ * @param s the string to encode
+ * @return the encoded string
+ */
+ public static String encode(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ String s = String.valueOf(value);
+ StringBuilder out = new StringBuilder();
+ boolean modified = false;
+
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+
+ if (c == '<') {
+ out.append("&lt;");
+ modified = true;
+ } else if (c == '>') {
+ out.append("&gt;");
+ modified = true;
+ } else if (c == '&') {
+ out.append("&amp;");
+ modified = true;
+ } else if (c < 32 || c > 126) {
+ out.append("&#" + (int)c + ";");
+ modified = true;
+ } else {
+ out.append(c);
+ }
+ }
+
+ if (modified) {
+ return out.toString();
+ } else {
+ return s;
+ }
+ }
+
+ /**
+ * Encodes a value so it can be used inside an XML attribute.
+ * @param s the string to encode
+ * @return the encoded string
+ */
+ public static String encodeAttr(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ String s = String.valueOf(value);
+ StringBuilder out = new StringBuilder();
+ boolean modified = false;
+
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+
+ if (c == '<') {
+ out.append("&lt;");
+ modified = true;
+ } else if (c == '>') {
+ out.append("&gt;");
+ modified = true;
+ } else if (c == '"') {
+ out.append("&quot;");
+ modified = true;
+ } else if (c == '&') {
+ out.append("&amp;");
+ modified = true;
+ } else if (c < 32 || c > 126) {
+ out.append("&#" + (int)c + ";");
+ modified = true;
+ } else {
+ out.append(c);
+ }
+ }
+
+ if (modified) {
+ return out.toString();
+ } else {
+ return s;
+ }
+ }
+
+ /**
+ * Decodes XML entities in a string value
+ * @param value a value with embedded XML entities
+ * @return the decoded string
+ */
+ public static String decode(Object value) {
+ if (value == null) {
+ return null;
+ }
+
+ String s = String.valueOf(value);
+
+ StringBuilder out = new StringBuilder(s.length());
+ int ampIndex = s.indexOf("&");
+ int lastEnd = 0;
+
+ while (ampIndex >= 0) {
+ int nextAmpIndex = s.indexOf("&", ampIndex + 1);
+ int nextSemiIndex = s.indexOf(";", ampIndex + 1);
+ if (nextSemiIndex != -1 && (nextAmpIndex == -1 || nextSemiIndex < nextAmpIndex)) {
+ int code = -1;
+ String entity = s.substring(ampIndex + 1, nextSemiIndex);
+
+ try {
+ if (entity.startsWith("#")) {
+ code = Integer.parseInt(entity.substring(1), 10);
+ } else {
+ if (ENTITIES.containsKey(entity)) {
+ code = ENTITIES.get(entity);
+ }
+ }
+ } catch (NumberFormatException x) {
+ // Do nothing
+ }
+
+ out.append(s.substring(lastEnd, ampIndex));
+ lastEnd = nextSemiIndex + 1;
+ if (code >= 0 && code <= 0xffff) {
+ out.append((char) code);
+ } else {
+ out.append("&");
+ out.append(entity);
+ out.append(";");
+ }
+ }
+
+ ampIndex = nextAmpIndex;
+ }
+
+ out.append(s.substring(lastEnd));
+ return out.toString();
+ }
+
+ /**
+ * Removes the preamble, if present, from an XML document.
+ * @param xml the XML document
+ * @return a possibly modified document
+ */
+ public static String removePreamble(Object xml) {
+ if (xml == null) {
+ return null;
+ }
+
+ return String.valueOf(xml).replaceAll("(<\\?[^<]*\\?>\\s*[\\r\\n]*)?", "");
+ }
+
+ /**
+ * Removes namespaces and namespace declarations from an XML document.
+ * @param xml the XML document
+ * @return a possibly modified document
+ */
+ public static String removeNamespaces(Object xml) {
+ if (xml == null) {
+ return null;
+ }
+
+ String text = String.valueOf(xml);
+
+ // remove xmlns declaration
+ text = text.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
+ // remove opening tag prefix
+ text = text.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
+ // remove closing tags prefix
+ text = text.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
+ // remove extra spaces left when xmlns declarations are removed
+ text = text.replaceAll("\\s+>", ">");
+
+ return text;
+ }
+
+
+ /**
+ * Reads the specified resource file and return the contents as a string.
+ * @param file Name of the resource file
+ * @return the contents of the resource file as a String
+ * @throws IOException if there is a problem reading the file
+ */
+ private static String readResourceFile(String file) throws IOException {
+ InputStream stream = null;
+ try {
+ stream = XmlTool.class.getClassLoader().getResourceAsStream(file);
+
+ if (stream == null) {
+ throw new FileNotFoundException("No such resource file: " + file);
+ }
+
+ Reader reader = new InputStreamReader(stream, "UTF-8");
+ StringBuilder out = new StringBuilder();
+ char[] buf = new char[1024];
+ int n;
+
+ while ((n = reader.read(buf)) >= 0) {
+ out.append(buf, 0, n);
+ }
+
+ stream.close();
+ stream = null;
+ return out.toString();
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Instantiation is not allowed.
+ */
+ private XmlTool() {
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/test/CamundaDBSetup.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/test/CamundaDBSetup.java
new file mode 100644
index 0000000000..13eed2d530
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/test/CamundaDBSetup.java
@@ -0,0 +1,108 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Sets up the unit test (H2) database for Camunda.
+ */
+public class CamundaDBSetup {
+ private static boolean isDBConfigured = false;
+
+ public static synchronized void configure() throws SQLException {
+ if (isDBConfigured) {
+ return;
+ }
+
+ System.out.println("Configuring the Camunda H2 database for MSO");
+
+ Connection connection = null;
+ PreparedStatement stmt = null;
+
+ try {
+ connection = DriverManager.getConnection(
+ "jdbc:h2:mem:camunda;DB_CLOSE_DELAY=-1", "sa", "");
+
+ stmt = connection.prepareStatement("delete from ACT_HI_VARINST");
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = null;
+
+ stmt = connection.prepareStatement("ALTER TABLE ACT_HI_VARINST alter column TEXT_ clob");
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = null;
+
+ stmt = connection.prepareStatement("ALTER TABLE ACT_HI_VARINST alter column NAME_ clob");
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = null;
+
+ stmt = connection.prepareStatement("delete from ACT_HI_DETAIL");
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = null;
+
+ stmt = connection.prepareStatement("ALTER TABLE ACT_HI_DETAIL alter column TEXT_ clob");
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = null;
+
+ stmt = connection.prepareStatement("ALTER TABLE ACT_HI_DETAIL alter column NAME_ clob");
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = null;
+
+ stmt = connection.prepareStatement("ALTER TABLE ACT_RU_VARIABLE alter column TEXT_ clob");
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = null;
+
+ connection.close();
+ connection = null;
+
+ isDBConfigured = true;
+ } catch (SQLException e) {
+ System.out.println("CamundaDBSetup caught " + e.getClass().getSimpleName());
+ e.printStackTrace();
+ } finally {
+ if (stmt != null) {
+ try {
+ stmt.close();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
+ if (connection != null) {
+ try {
+ connection.close();
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ }
+ }
+}
diff --git a/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/test/PropertyConfigurationSetup.java b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/test/PropertyConfigurationSetup.java
new file mode 100644
index 0000000000..6f1cd7d8cc
--- /dev/null
+++ b/bpmn/MSOCoreBPMN/src/main/java/org/openecomp/mso/bpmn/test/PropertyConfigurationSetup.java
@@ -0,0 +1,315 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * OPENECOMP - MSO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.mso.bpmn.test;
+
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.openecomp.mso.bpmn.core.PropertyConfiguration;
+
+/**
+ * Sets up mso.bpmn.properties and mso.bpmn.urn.properties for unit tests.
+ */
+public class PropertyConfigurationSetup {
+
+ private static Path msoConfigPath = null;
+ private static Path bpmnPropertiesPath = null;
+ private static Path bpmnUrnPropertiesPath = null;
+ private static boolean modifiedConfiguration = false;
+
+ /**
+ * Ensures that the the PropertyConfiguration is initialized and that the
+ * property data is reset to initial values. Any extra properties that are
+ * specified will be merged with the initial values. The following example
+ * shows how a test can specify a replacement URN mapping property.
+ * <pre>
+ * Map<String, String> urnProperties =
+ * PropertyConfigurationSetup.createBpmnUrnProperties();
+ * urnProperties.add("mso.po.timeout", "PT1M");
+ * PropertyConfiguration.init(urnProperties);
+ * </pre>
+ * @param args one or more maps created with createBpmnProperties()
+ * and/or createBpmnUrnProperties()
+ */
+ public static synchronized void init(Object ... args) throws IOException {
+
+ Map<String, String> extraBpmnProperties = null;
+ Map<String, String> extraBpmnUrnProperties = null;
+
+ boolean propertiesSpecified = false;
+
+ for (Object arg : args) {
+ @SuppressWarnings("unchecked")
+ Map<String, String> properties = (Map<String, String>) arg;
+
+ String type = properties.get("PROPERTIES-TYPE");
+
+ if (PropertyConfiguration.MSO_BPMN_PROPERTIES.equals(type)) {
+ if (properties.size() > 1) {
+ extraBpmnProperties = properties;
+ propertiesSpecified = false;
+ }
+ } else if (PropertyConfiguration.MSO_BPMN_URN_PROPERTIES.equals(type)) {
+ if (properties.size() > 1) {
+ extraBpmnUrnProperties = properties;
+ propertiesSpecified = false;
+ }
+ } else {
+ throw new IllegalArgumentException("Not a supported PROPERTIES-TYPE map");
+ }
+ }
+
+ // There are three cases in which we need to change the existing configuration:
+ // 1) There is no existing configuration, i.e. first time setup
+ // 2) The existing configuration was modified, i.e. it has non-default values
+ // 3) Non-default values are specified for this initialization
+
+ if (msoConfigPath == null || modifiedConfiguration || propertiesSpecified) {
+ modifiedConfiguration = propertiesSpecified;
+
+ Path bpmnPropertiesSourcePath = Paths.get("src", "test", "resources", "mso.bpmn.properties");
+ Path bpmnUrnPropertiesSourcePath = Paths.get("src", "test", "resources", "mso.bpmn.urn.properties");
+
+ if (msoConfigPath == null) {
+ // Initialize from scratch.
+ msoConfigPath = Files.createTempDirectory("mso-config-path-");
+ System.setProperty("mso.config.path", msoConfigPath.toString());
+ msoConfigPath.toFile().deleteOnExit();
+
+ bpmnPropertiesPath = msoConfigPath.resolve("mso.bpmn.properties");
+ mergeCopy(bpmnPropertiesSourcePath, extraBpmnProperties, bpmnPropertiesPath);
+ bpmnPropertiesPath.toFile().deleteOnExit();
+
+ bpmnUrnPropertiesPath = msoConfigPath.resolve("mso.bpmn.urn.properties");
+ mergeCopy(bpmnUrnPropertiesSourcePath, extraBpmnUrnProperties, bpmnUrnPropertiesPath);
+ bpmnUrnPropertiesPath.toFile().deleteOnExit();
+
+ PropertyConfiguration.getInstance();
+ } else {
+ // Just reset the data.
+ PropertyConfiguration.getInstance().clearCache();
+ mergeCopy(bpmnPropertiesSourcePath, extraBpmnProperties, bpmnPropertiesPath);
+ mergeCopy(bpmnUrnPropertiesSourcePath, extraBpmnUrnProperties, bpmnUrnPropertiesPath);
+ }
+ }
+ }
+
+ /**
+ * Resets the PropertyConfiguration to its initial state, as if it had never
+ * been started. Note that this is a very expensive option and should not
+ * be needed by most unit tests.
+ * @throws IOException
+ */
+ public static synchronized void nuke() throws IOException {
+ if (msoConfigPath == null) {
+ return;
+ }
+
+ PropertyConfiguration.getInstance().shutDown();
+
+ bpmnUrnPropertiesPath.toFile().delete();
+ bpmnUrnPropertiesPath = null;
+
+ bpmnPropertiesPath.toFile().delete();
+ bpmnPropertiesPath = null;
+
+ msoConfigPath.toFile().delete();
+ msoConfigPath = null;
+
+ System.setProperty("mso.config.path", null);
+
+ modifiedConfiguration = false;
+ }
+
+ /**
+ * Create a map to hold properties to be added to mso.bpmn.properties.
+ */
+ public static Map<String, String> createBpmnProperties() {
+ Map<String, String> properties = new HashMap<String, String>();
+ properties.put("PROPERTIES-TYPE", PropertyConfiguration.MSO_BPMN_PROPERTIES);
+ return properties;
+ }
+
+ /**
+ * Create a map to hold properties to be added to mso.bpmn.urn.properties.
+ */
+ public static Map<String, String> createBpmnUrnProperties() {
+ Map<String, String> properties = new HashMap<String, String>();
+ properties.put("PROPERTIES-TYPE", PropertyConfiguration.MSO_BPMN_URN_PROPERTIES);
+ return properties;
+ }
+
+ /**
+ * Adds (or replaces) the specified values in the mso.bpmn.urn.properties file.
+ * Note that properties added this way may take some time to be loaded by the
+ * PropertyConfiguration, just like they do when a property file is updated on
+ * a real MSO system. This method will optionally wait for the new properties
+ * to be loaded. Timeout results in an IOException.
+ * @param values new properties
+ * @param wait maximum amount of time to wait for new properties to be loaded,
+ * in milliseconds. A value of zero means, "Do not wait."
+ * @throws IOException
+ */
+ public static synchronized void addProperties(Map<String, String> properties, long wait)
+ throws IOException, InterruptedException {
+
+ if (msoConfigPath == null) {
+ throw new IllegalStateException();
+ }
+
+ String type = properties.get("PROPERTIES-TYPE");
+ Path path;
+
+ if (PropertyConfiguration.MSO_BPMN_PROPERTIES.equals(type)) {
+ path = bpmnPropertiesPath;
+ } else if (PropertyConfiguration.MSO_BPMN_URN_PROPERTIES.equals(type)) {
+ path = bpmnUrnPropertiesPath;
+ } else {
+ throw new IllegalArgumentException("Not a supported PROPERTIES-TYPE map");
+ }
+
+ String oldTimestamp = PropertyConfiguration.getInstance().getProperties(type)
+ .get(PropertyConfiguration.TIMESTAMP_PROPERTY);
+
+ modifiedConfiguration = true;
+ addProperties(properties, path);
+
+ if (wait <= 0) {
+ return;
+ }
+
+ long endTime = System.currentTimeMillis() + wait;
+
+ while (true) {
+ Thread.sleep(250);
+
+ String newTimestamp = PropertyConfiguration.getInstance().getProperties(type)
+ .get(PropertyConfiguration.TIMESTAMP_PROPERTY);
+
+ if (newTimestamp != oldTimestamp) {
+ return;
+ }
+
+ long now = System.currentTimeMillis();
+
+ if (now >= endTime) {
+ throw new IOException("Timed out after " + wait
+ + "ms waiting for PropertyConfiguration change");
+ }
+ }
+ }
+
+ /**
+ * Helper method that adds properties to the specified file.
+ */
+ private static void addProperties(Map<String, String> values, Path path)
+ throws IOException {
+
+ FileReader fileReader = null;
+ FileOutputStream outputStream = null;
+
+ try {
+ fileReader = new FileReader(path.toFile());
+ Properties properties = new Properties();
+ properties.load(fileReader);
+
+ for (String key : values.keySet()) {
+ if (!key.equals("PROPERTIES-TYPE")) {
+ properties.setProperty(key, values.get(key));
+ }
+ }
+
+ outputStream = new FileOutputStream(path.toFile());
+ properties.store(outputStream, "Custom Test Properties");
+ } finally {
+ if (fileReader != null) {
+ try {
+ fileReader.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper method that copies properties from the specified source file, and
+ * optionally merges them with the specified extra values, then writes the
+ * whole mess to the destination file.
+ */
+ private static void mergeCopy(Path sourcePath, Map<String, String> extraValues, Path destPath)
+ throws IOException {
+ if (extraValues == null || extraValues.isEmpty()) {
+ Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING);
+ return;
+ }
+
+ FileReader fileReader = null;
+ FileOutputStream outputStream = null;
+
+ try {
+ fileReader = new FileReader(sourcePath.toFile());
+ Properties properties = new Properties();
+ properties.load(fileReader);
+
+ for (String key : extraValues.keySet()) {
+ if (!key.equals("PROPERTIES-TYPE")) {
+ properties.setProperty(key, extraValues.get(key));
+ }
+ }
+
+ outputStream = new FileOutputStream(destPath.toFile());
+ properties.store(outputStream, "Custom Test Properties");
+ } finally {
+ if (fileReader != null) {
+ try {
+ fileReader.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+}