6080768cee4792c3e24668d09d63c515c9b10560
[so.git] / bpmn / MSOCoreBPMN / src / main / java / org / openecomp / mso / bpmn / core / XQueryScriptTask.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright (C) 2017 Huawei Technologies Co., Ltd. All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.openecomp.mso.bpmn.core;
23
24 import java.io.ByteArrayInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.math.BigDecimal;
28 import java.net.URI;
29 import java.util.Iterator;
30
31 import javax.xml.transform.stream.StreamSource;
32
33 import org.camunda.bpm.engine.ProcessEngineException;
34 import org.camunda.bpm.engine.delegate.DelegateExecution;
35 //import java.util.logging.Logger;
36 import org.camunda.bpm.engine.delegate.Expression;
37
38 import org.openecomp.mso.logger.MessageEnum;
39 import org.openecomp.mso.logger.MsoLogger;
40
41 import net.sf.saxon.Configuration;
42 import net.sf.saxon.s9api.DocumentBuilder;
43 import net.sf.saxon.s9api.Processor;
44 import net.sf.saxon.s9api.QName;
45 import net.sf.saxon.s9api.XQueryCompiler;
46 import net.sf.saxon.s9api.XQueryEvaluator;
47 import net.sf.saxon.s9api.XQueryExecutable;
48 import net.sf.saxon.s9api.XdmAtomicValue;
49 import net.sf.saxon.s9api.XdmItem;
50 import net.sf.saxon.s9api.XdmNode;
51
52 /**
53  * Executes an XQuery script.
54  * <p>
55  * Required fields:<br/><br/>
56  * &nbsp;&nbsp;&nbsp;&nbsp;scriptFile: the XQuery script file path<br/>
57  * &nbsp;&nbsp;&nbsp;&nbsp;outputVariable: the output variable name<br/>
58  * <p>
59  * Optional fields:<br/><br/>
60  * &nbsp;&nbsp;&nbsp;&nbsp;xmlInputVariables: CSV list of variables containing
61  *              XML data to be injected into the script<br/>
62  * &nbsp;&nbsp;&nbsp;&nbsp;atomicInputVariables: CSV list of variables containing
63  *              atomic data to be injected into the script<br/>
64  */
65 public class XQueryScriptTask extends BaseTask {
66         
67         private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
68
69         private Expression scriptFile;
70         private Expression xmlInputVariables;
71         private Expression atomicInputVariables;
72         private Expression outputVariable;
73
74         public void execute(DelegateExecution execution) throws Exception {
75                 if (msoLogger.isDebugEnabled()) {
76                         msoLogger.debug("Started Executing " + getTaskName());
77                 }
78
79                 String theScriptFile =
80                         getStringField(scriptFile, execution, "scriptFile");
81                 String theXmlInputVariables =
82                         getOptionalStringField(xmlInputVariables, execution, "xmlInputVariables");
83                 String theAtomicInputVariables =
84                         getOptionalStringField(atomicInputVariables, execution, "atomicInputVariables");
85                 String theOutputVariable =
86                         getStringField(outputVariable, execution, "outputVariable");
87
88                 if (msoLogger.isDebugEnabled()) {
89                     msoLogger.debug ("scriptFile = " + theScriptFile
90                                 + " xmlInputVariables = " + theXmlInputVariables
91                                 + " atomicInputVariables = " + theAtomicInputVariables
92                                 + "outputVariable = " + theOutputVariable);
93                 }
94
95                 String[] xmlInputVariableArray = (theXmlInputVariables == null)
96                         ? new String[0] : theXmlInputVariables.split(",[ ]*");
97
98                 String[] atomicInputVariableArray = (theAtomicInputVariables == null)
99                         ? new String[0] : theAtomicInputVariables.split(",[ ]*");
100
101                 Boolean shouldFail = (Boolean) execution.getVariable("shouldFail");
102
103                 if (shouldFail != null && shouldFail) {
104                         throw new ProcessEngineException(getClass().getSimpleName() + " Failed");
105                 }
106
107                 // The script could be compiled once and reused, but we are reading it
108                 // and compiling it every time.
109                 Configuration configuration = new Configuration();
110                 Processor processor = new Processor(configuration);
111                 XQueryCompiler compiler = processor.newXQueryCompiler();
112                 XQueryExecutable executable = compile(compiler, theScriptFile);
113                 if (executable == null) {
114                     throw new ProcessEngineException(getClass().getSimpleName() + " Failed");
115                 }
116
117                 // The evaluator must not be shared by multiple threads.  Here is where
118                 // the initial context may be set, as well as values of external variables.
119                 XQueryEvaluator evaluator = executable.load();
120
121                 // Convert XML string variable content to document-node objects and inject
122                 // these into the evaluator.  Note: the script must accept the document-node
123                 // type.  Most MSO scripts today expect element() input, not document-node
124                 // input.  TODO: figure out how to pass the variable data as element() types.
125
126                 for (String xmlInputVariable : xmlInputVariableArray) {
127                         if (msoLogger.isDebugEnabled()) {
128                                 msoLogger.debug("Injecting XML variable '" + xmlInputVariable + "'");
129                                 msoLogger.debug("printing the variable content>>'" + execution.getVariable(xmlInputVariable) +"'");
130                         }
131
132                         String xml = (String) execution.getVariable(xmlInputVariable);
133                         DocumentBuilder documentBuilder = processor.newDocumentBuilder();
134                         StreamSource source = new StreamSource(new ByteArrayInputStream(xml.getBytes("UTF-8")));
135                         XdmNode xdmNode = documentBuilder.build(source);
136
137                         // Inject the document-node object into the XQueryEvaluator.
138                         // TODO: transform it to an element()
139                         QName variable = new QName(xmlInputVariable);
140                         evaluator.setExternalVariable(variable, xdmNode);
141                 }
142
143                 // Inject atomic variables into the evaluator.
144
145                 for (String atomicInputVariable : atomicInputVariableArray) {
146                         
147                         if (msoLogger.isDebugEnabled()) {
148                             msoLogger.debug ("Injecting object variable '"
149                                         + atomicInputVariable + "'");
150                         }
151
152                         QName variable = new QName(atomicInputVariable);
153                         Object value = execution.getVariable(atomicInputVariable);
154
155                         if (value == null) {
156                                 // The variable value is null, so we have no way to know what
157                                 // type it is.  I don't know how to deal with this, so for
158                                 // now, just skip it.
159                                 
160                                 msoLogger.warn (MessageEnum.BPMN_VARIABLE_NULL, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError, atomicInputVariable);
161                                 
162                                 continue;
163                         }
164
165                         // There might be a better way to do this...
166                         if (value instanceof BigDecimal) {
167                                 evaluator.setExternalVariable(variable,
168                                         new XdmAtomicValue((BigDecimal) value));
169                         } else if (value instanceof Boolean) {
170                                 evaluator.setExternalVariable(variable,
171                                         new XdmAtomicValue((Boolean) value));
172                         } else if (value instanceof Double) {
173                                 evaluator.setExternalVariable(variable,
174                                         new XdmAtomicValue((Double) value));
175                         } else if (value instanceof Float) {
176                                 evaluator.setExternalVariable(variable,
177                                         new XdmAtomicValue((Float) value));
178                         } else if (value instanceof Long) {
179                                 evaluator.setExternalVariable(variable,
180                                         new XdmAtomicValue((Long) value));
181                         } else if (value instanceof String) {
182                                 evaluator.setExternalVariable(variable,
183                                         new XdmAtomicValue((String) value));
184                         } else if (value instanceof URI) {
185                                 evaluator.setExternalVariable(variable,
186                                         new XdmAtomicValue((URI) value));
187                         } else {
188                                 throw new BadInjectedFieldException(
189                                         "atomicInputVariables", getTaskName(),
190                                         "'" + atomicInputVariable + "' type is not supported: "
191                                                 + value.getClass());
192                         }
193                 }
194
195                 // Evaluate the query and collect the output.
196                 StringBuilder output = new StringBuilder();
197                 Iterator<XdmItem> xdmItems = evaluator.iterator();
198                 while (xdmItems.hasNext()) {
199                         XdmItem item = xdmItems.next();
200                         
201                         if (msoLogger.isDebugEnabled()) {
202                                 msoLogger.debug("XQuery result item = " + item);
203                         }
204
205                         output.append(item.toString());
206                 }
207
208                 // Set the output variable.
209                 execution.setVariable(theOutputVariable, output.toString());
210
211                 if (msoLogger.isDebugEnabled()) {
212                         msoLogger.debug("Done Executing " + getTaskName());
213                 }
214         }
215         
216         /**
217          * Compiles an XQuery script contained in a resource (file).
218          * @param compiler the XQueryCompiler
219          * @param resource the resource path
220          * @return an XQueryExecutable
221          * @throws Exception on error
222          */
223         private XQueryExecutable compile(XQueryCompiler compiler, String resource)
224                         throws Exception {
225             try (InputStream xqStream = getClass().getResourceAsStream(resource)) {
226                 XQueryExecutable executable = compiler.compile(xqStream);
227                 return executable;
228             } catch (Exception e) {
229                 msoLogger.debug ("Exception at resourceFile stream:", e);
230                 return null;
231             }
232         }
233 }