2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Modifications Copyright (c) 2019 Samsung
8 * ================================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END=========================================================
23 package org.onap.so.bpmn.core;
25 import org.camunda.bpm.engine.delegate.DelegateExecution;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
30 * Used in the output variable mapping configuration of subflow call activity
31 * tasks to normalize subflow responses. The output mapping is normally set up
32 * as follows. Note that the order of these mappings is important!
36 * SOURCE EXPRESSION TARGET
37 * ${ResponseBuilder.buildWorkflowException(execution)} WorkflowException
38 * ${ResponseBuilder.buildWorkflowResponse(execution)} SomeResponseVariable
41 public class ResponseBuilder implements java.io.Serializable {
42 private static final long serialVersionUID = 1L;
43 private static final Logger logger = LoggerFactory.getLogger(ResponseBuilder.class);
46 * Creates a WorkflowException using data from the execution variables.
47 * If the variables do not indicate that there was an error, null
49 * @param execution the execution
51 public WorkflowException buildWorkflowException(DelegateExecution execution) {
53 String method = getClass().getSimpleName() + ".buildWorkflowException(" +
54 "execution=" + execution.getId() +
57 logger.debug("Entered " + method);
59 String prefix = (String) execution.getVariable("prefix");
60 String processKey = getProcessKey(execution);
62 logger.debug("processKey=" + processKey);
64 // See if there"s already a WorkflowException object in the execution.
65 WorkflowException theException = (WorkflowException) execution.getVariable("WorkflowException");
67 if (theException != null) {
68 logger.debug("Exited " + method + " - propagated " + theException);
72 // Look in the legacy variables: ErrorResponse and ResponseCode
74 String errorResponse = trimString(execution.getVariable(prefix + "ErrorResponse"), null);
75 String responseCode = trimString(execution.getVariable(prefix + "ResponseCode"), null);
76 logger.debug("errorResponse=" + errorResponse);
77 logger.debug("responseCode=" + responseCode);
78 if (errorResponse != null || !isOneOf(responseCode, null, "0", "200", "201", "202", "204")) {
79 // This is an error condition. We need to return a WorkflowExcpetion
81 if (errorResponse == null) {
82 // No errorResponse string. See if there"s something in the Response variable
83 String response = trimString(execution.getVariable(processKey + "Response"), null);
84 if (response == null) {
85 errorResponse = "Received response code " + responseCode + " from " + processKey;
87 errorResponse = response;
91 // Some subflows may try to return a WorkflowException as XML in the
92 // errorResponse. If provided, use the errorCode and errorMessage
95 String maybeXML = removeXMLNamespaces(errorResponse);
97 String xmlErrorMessage = trimString(getXMLTextElement(maybeXML, "ErrorMessage"), null);
98 String xmlErrorCode = trimString(getXMLTextElement(maybeXML, "ErrorCode"), null);
100 if (xmlErrorMessage != null || xmlErrorCode != null) {
101 logger.debug("xmlErrorMessage=" + xmlErrorMessage);
102 logger.debug("xmlErrorCode=" + xmlErrorCode);
104 if (xmlErrorMessage == null) {
105 errorResponse = "Received error code " + xmlErrorCode + " from " + processKey;
107 errorResponse = xmlErrorMessage;
110 if (xmlErrorCode != null) {
111 responseCode = xmlErrorCode;
115 // Convert the responseCode to an integer
120 intResponseCode = Integer.valueOf(responseCode);
121 } catch (NumberFormatException e) {
123 intResponseCode = 2000;
126 // Convert 3-digit HTTP response codes (we should not be using them here)
127 // to appropriate 4-digit response codes
129 if (intResponseCode < 1000) {
130 if (intResponseCode >= 400 && intResponseCode <= 499) {
132 intResponseCode = 1002;
135 intResponseCode = 2000;
139 // Create a new WorkflowException object
141 theException = new WorkflowException(processKey, intResponseCode, errorResponse);
142 execution.setVariable("WorkflowException", theException);
143 logger.debug("Exited " + method + " - created " + theException);
147 logger.debug("Exited " + method + " - no WorkflowException");
152 * Returns the "Response" variable, unless the execution variables
153 * indicate there was an error. In that case, null is returned.
154 * @param execution the execution
156 public Object buildWorkflowResponse(DelegateExecution execution) {
158 String method = getClass().getSimpleName() + ".buildWorkflowResponse(" +
159 "execution=" + execution.getId() +
161 logger.debug("Entered " + method);
163 String prefix = (String) execution.getVariable("prefix");
164 String processKey = getProcessKey(execution);
166 Object theResponse = null;
168 WorkflowException theException = (WorkflowException) execution.getVariable("WorkflowException");
169 String errorResponse = trimString(execution.getVariable(prefix + "ErrorResponse"), null);
170 String responseCode = trimString(execution.getVariable(prefix + "ResponseCode"), null);
172 if (theException == null && errorResponse == null &&
173 isOneOf(responseCode, null, "0", "200", "201", "202", "204")) {
175 theResponse = execution.getVariable("WorkflowResponse");
177 if (theResponse == null) {
178 theResponse = execution.getVariable(processKey + "Response");
182 logger.debug("Exited " + method);
187 * Checks if the specified item is one of the specified values.
188 * @param item the item
189 * @param values the list of values
190 * @return true if the item is in the list of values
192 private boolean isOneOf(Object item, Object ... values) {
193 if (values == null) {
197 for (Object value : values) {
203 if (value.equals(item)) {
213 * Creates a string value of the specified object, trimming whitespace in
214 * the process. If the result is null or empty, the specified empty string
215 * value is returned. Otherwise the trimmed value is returned. This method
216 * helps ensure consistent treatment of empty and null strings.
217 * @param object the object to convert (possibly null)
218 * @param emptyStringValue the desired value for empty results
220 private String trimString(Object object, String emptyStringValue) {
221 if (object == null) {
222 return emptyStringValue;
225 String s = String.valueOf(object).trim();
226 return s.equals("") ? emptyStringValue : s;
230 * Returns the process definition key (i.e. the process name) from the
232 * @param execution the execution
234 private String getProcessKey(DelegateExecution execution) {
235 Object testKey = execution.getVariable("testProcessKey");
237 if (testKey instanceof String) {
238 return (String) testKey;
241 return execution.getProcessEngineServices().getRepositoryService()
242 .getProcessDefinition(execution.getProcessDefinitionId()).getKey();
246 * Removes namespace definitions and prefixes from XML, if any.
248 private String removeXMLNamespaces(String xml) {
249 // remove xmlns declaration
250 xml = xml.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
252 // remove opening tag prefix
253 xml = xml.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
255 // remove closing tags prefix
256 xml = xml.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
258 // remove extra spaces left when xmlns declarations are removed
259 xml = xml.replaceAll("\\s+>", ">");
265 * Extracts text from an XML element. This method is not namespace aware
266 * (namespaces are ignored). The first matching element is selected.
267 * @param xml the XML document or fragment
268 * @param tag the desired element, e.g. "<name>"
269 * @return the element text, or null if the element was not found
271 private String getXMLTextElement(String xml, String tag) {
272 xml = removeXMLNamespaces(xml);
274 if (!tag.startsWith("<")) {
275 tag = "<" + tag + ">";
278 int start = xml.indexOf(tag);
284 int end = xml.indexOf('<', start + tag.length());
290 return xml.substring(start + tag.length(), end);