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