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