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 tasks to normalize subflow responses. The
31 * output mapping is normally set up 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. If the variables do not indicate that there
47 * was an error, null is returned.
49 * @param execution the execution
51 public WorkflowException buildWorkflowException(DelegateExecution execution) {
54 getClass().getSimpleName() + ".buildWorkflowException(" + "execution=" + execution.getId() + ")";
56 logger.debug("Entered " + method);
58 String prefix = (String) execution.getVariable("prefix");
59 String processKey = getProcessKey(execution);
61 logger.debug("processKey=" + processKey);
63 // See if there"s already a WorkflowException object in the execution.
64 WorkflowException theException = (WorkflowException) execution.getVariable("WorkflowException");
66 if (theException != null) {
67 logger.debug("Exited " + method + " - propagated " + theException);
71 // Look in the legacy variables: ErrorResponse and ResponseCode
73 String errorResponse = trimString(execution.getVariable(prefix + "ErrorResponse"), null);
74 String responseCode = trimString(execution.getVariable(prefix + "ResponseCode"), null);
75 logger.debug("errorResponse=" + errorResponse);
76 logger.debug("responseCode=" + responseCode);
77 if (errorResponse != null || !isOneOf(responseCode, null, "0", "200", "201", "202", "204")) {
78 // This is an error condition. We need to return a WorkflowExcpetion
80 if (errorResponse == null) {
81 // No errorResponse string. See if there"s something in the Response variable
82 String response = trimString(execution.getVariable(processKey + "Response"), null);
83 if (response == null) {
84 errorResponse = "Received response code " + responseCode + " from " + processKey;
86 errorResponse = response;
90 // Some subflows may try to return a WorkflowException as XML in the
91 // errorResponse. If provided, use the errorCode and errorMessage
94 String maybeXML = removeXMLNamespaces(errorResponse);
96 String xmlErrorMessage = trimString(getXMLTextElement(maybeXML, "ErrorMessage"), null);
97 String xmlErrorCode = trimString(getXMLTextElement(maybeXML, "ErrorCode"), null);
99 if (xmlErrorMessage != null || xmlErrorCode != null) {
100 logger.debug("xmlErrorMessage=" + xmlErrorMessage);
101 logger.debug("xmlErrorCode=" + xmlErrorCode);
103 if (xmlErrorMessage == null) {
104 errorResponse = "Received error code " + xmlErrorCode + " from " + processKey;
106 errorResponse = xmlErrorMessage;
109 if (xmlErrorCode != null) {
110 responseCode = xmlErrorCode;
114 // Convert the responseCode to an integer
119 intResponseCode = Integer.valueOf(responseCode);
120 } catch (NumberFormatException e) {
122 intResponseCode = 2000;
125 // Convert 3-digit HTTP response codes (we should not be using them here)
126 // to appropriate 4-digit response codes
128 if (intResponseCode < 1000) {
129 if (intResponseCode >= 400 && intResponseCode <= 499) {
131 intResponseCode = 1002;
134 intResponseCode = 2000;
138 // Create a new WorkflowException object
140 theException = new WorkflowException(processKey, intResponseCode, errorResponse);
141 execution.setVariable("WorkflowException", theException);
142 logger.debug("Exited " + method + " - created " + theException);
146 logger.debug("Exited " + method + " - no WorkflowException");
151 * Returns the "Response" variable, unless the execution variables indicate there was an error. In that case, null
154 * @param execution the execution
156 public Object buildWorkflowResponse(DelegateExecution execution) {
158 String method = getClass().getSimpleName() + ".buildWorkflowResponse(" + "execution=" + execution.getId() + ")";
159 logger.debug("Entered " + method);
161 String prefix = (String) execution.getVariable("prefix");
162 String processKey = getProcessKey(execution);
164 Object theResponse = null;
166 WorkflowException theException = (WorkflowException) execution.getVariable("WorkflowException");
167 String errorResponse = trimString(execution.getVariable(prefix + "ErrorResponse"), null);
168 String responseCode = trimString(execution.getVariable(prefix + "ResponseCode"), null);
170 if (theException == null && errorResponse == null
171 && isOneOf(responseCode, null, "0", "200", "201", "202", "204")) {
173 theResponse = execution.getVariable("WorkflowResponse");
175 if (theResponse == null) {
176 theResponse = execution.getVariable(processKey + "Response");
180 logger.debug("Exited " + method);
185 * Checks if the specified item is one of the specified values.
187 * @param item the item
188 * @param values the list of values
189 * @return true if the item is in the list of values
191 private boolean isOneOf(Object item, Object... values) {
192 if (values == null) {
196 for (Object value : values) {
202 if (value.equals(item)) {
212 * Creates a string value of the specified object, trimming whitespace in the process. If the result is null or
213 * empty, the specified empty string value is returned. Otherwise the trimmed value is returned. This method helps
214 * ensure consistent treatment of empty and null strings.
216 * @param object the object to convert (possibly null)
217 * @param emptyStringValue the desired value for empty results
219 private String trimString(Object object, String emptyStringValue) {
220 if (object == null) {
221 return emptyStringValue;
224 String s = String.valueOf(object).trim();
225 return s.equals("") ? emptyStringValue : s;
229 * Returns the process definition key (i.e. the process name) from the execution.
231 * @param execution the execution
233 private String getProcessKey(DelegateExecution execution) {
234 Object testKey = execution.getVariable("testProcessKey");
236 if (testKey instanceof String) {
237 return (String) testKey;
240 return execution.getProcessEngineServices().getRepositoryService()
241 .getProcessDefinition(execution.getProcessDefinitionId()).getKey();
245 * Removes namespace definitions and prefixes from XML, if any.
247 private String removeXMLNamespaces(String xml) {
248 // remove xmlns declaration
249 xml = xml.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
251 // remove opening tag prefix
252 xml = xml.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
254 // remove closing tags prefix
255 xml = xml.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
257 // remove extra spaces left when xmlns declarations are removed
258 xml = xml.replaceAll("\\s+>", ">");
264 * Extracts text from an XML element. This method is not namespace aware (namespaces are ignored). The first
265 * 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);