e42806e95ef2b2c172ee9c02edc20fa442559c66
[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 \r
29 import javax.xml.transform.stream.StreamSource;\r
30 \r
31 import org.camunda.bpm.engine.ProcessEngineException;\r
32 import org.camunda.bpm.engine.delegate.DelegateExecution;\r
33 //import java.util.logging.Logger;\r
34 import org.camunda.bpm.engine.delegate.Expression;\r
35 import org.openecomp.mso.logger.MessageEnum;\r
36 import org.openecomp.mso.logger.MsoLogger;\r
37 \r
38 import net.sf.saxon.Configuration;\r
39 import net.sf.saxon.s9api.DocumentBuilder;\r
40 import net.sf.saxon.s9api.Processor;\r
41 import net.sf.saxon.s9api.QName;\r
42 import net.sf.saxon.s9api.XQueryCompiler;\r
43 import net.sf.saxon.s9api.XQueryEvaluator;\r
44 import net.sf.saxon.s9api.XQueryExecutable;\r
45 import net.sf.saxon.s9api.XdmAtomicValue;\r
46 import net.sf.saxon.s9api.XdmItem;\r
47 import net.sf.saxon.s9api.XdmNode;\r
48 \r
49 /**\r
50  * Executes an XQuery script.\r
51  * <p>\r
52  * Required fields:<br/><br/>\r
53  * &nbsp;&nbsp;&nbsp;&nbsp;scriptFile: the XQuery script file path<br/>\r
54  * &nbsp;&nbsp;&nbsp;&nbsp;outputVariable: the output variable name<br/>\r
55  * <p>\r
56  * Optional fields:<br/><br/>\r
57  * &nbsp;&nbsp;&nbsp;&nbsp;xmlInputVariables: CSV list of variables containing\r
58  *              XML data to be injected into the script<br/>\r
59  * &nbsp;&nbsp;&nbsp;&nbsp;atomicInputVariables: CSV list of variables containing\r
60  *              atomic data to be injected into the script<br/>\r
61  */\r
62 public class XQueryScriptTask extends BaseTask {\r
63         \r
64         private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);\r
65 \r
66         private Expression scriptFile;\r
67         private Expression xmlInputVariables;\r
68         private Expression atomicInputVariables;\r
69         private Expression outputVariable;\r
70 \r
71         public void execute(DelegateExecution execution) throws Exception {\r
72                 if (msoLogger.isDebugEnabled()) {\r
73                         msoLogger.debug("Started Executing " + getTaskName());\r
74                 }\r
75 \r
76                 String theScriptFile =\r
77                         getStringField(scriptFile, execution, "scriptFile");\r
78                 String theXmlInputVariables =\r
79                         getOptionalStringField(xmlInputVariables, execution, "xmlInputVariables");\r
80                 String theAtomicInputVariables =\r
81                         getOptionalStringField(atomicInputVariables, execution, "atomicInputVariables");\r
82                 String theOutputVariable =\r
83                         getStringField(outputVariable, execution, "outputVariable");\r
84 \r
85                 if (msoLogger.isDebugEnabled()) {\r
86                         System.out.println("scriptFile = " + theScriptFile\r
87                                 + " xmlInputVariables = " + theXmlInputVariables\r
88                                 + " atomicInputVariables = " + theAtomicInputVariables\r
89                                 + "outputVariable = " + theOutputVariable);\r
90                 }\r
91 \r
92                 String[] xmlInputVariableArray = (theXmlInputVariables == null)\r
93                         ? new String[0] : theXmlInputVariables.split(",[ ]*");\r
94 \r
95                 String[] atomicInputVariableArray = (theAtomicInputVariables == null)\r
96                         ? new String[0] : theAtomicInputVariables.split(",[ ]*");\r
97 \r
98         if (shouldFail(execution)) {\r
99             throw new ProcessEngineException(getTaskName() + " Failed");\r
100         }\r
101 \r
102                 // The script could be compiled once and reused, but we are reading it\r
103                 // and compiling it every time.\r
104                 Configuration configuration = new Configuration();\r
105                 Processor processor = new Processor(configuration);\r
106                 XQueryCompiler compiler = processor.newXQueryCompiler();\r
107                 XQueryExecutable executable = compile(compiler, theScriptFile);\r
108 \r
109                 // The evaluator must not be shared by multiple threads.  Here is where\r
110                 // the initial context may be set, as well as values of external variables.\r
111                 XQueryEvaluator evaluator = executable.load();\r
112 \r
113                 // Convert XML string variable content to document-node objects and inject\r
114                 // these into the evaluator.  Note: the script must accept the document-node\r
115                 // type.  Most MSO scripts today expect element() input, not document-node\r
116                 // input.  TODO: figure out how to pass the variable data as element() types.\r
117 \r
118                 for (String xmlInputVariable : xmlInputVariableArray) {\r
119                         if (msoLogger.isDebugEnabled()) {\r
120                                 msoLogger.debug("Injecting XML variable '" + xmlInputVariable + "'");\r
121                                 msoLogger.debug("printing the variable content>>'" + execution.getVariable(xmlInputVariable) +"'");\r
122                         }\r
123 \r
124                         String xml = (String) execution.getVariable(xmlInputVariable);\r
125                         DocumentBuilder documentBuilder = processor.newDocumentBuilder();\r
126                         StreamSource source = new StreamSource(new ByteArrayInputStream(xml.getBytes("UTF-8")));\r
127                         XdmNode xdmNode = documentBuilder.build(source);\r
128 \r
129                         // Inject the document-node object into the XQueryEvaluator.\r
130                         // TODO: transform it to an element()\r
131                         QName variable = new QName(xmlInputVariable);\r
132                         evaluator.setExternalVariable(variable, xdmNode);\r
133                 }\r
134 \r
135                 // Inject atomic variables into the evaluator.\r
136 \r
137                 for (String atomicInputVariable : atomicInputVariableArray) {\r
138                         \r
139                         if (msoLogger.isDebugEnabled()) {\r
140                                 System.out.println("Injecting object variable '"\r
141                                         + atomicInputVariable + "'");\r
142                         }\r
143 \r
144                         QName variable = new QName(atomicInputVariable);\r
145                         Object value = execution.getVariable(atomicInputVariable);\r
146 \r
147                         if (value == null) {\r
148                                 // The variable value is null, so we have no way to know what\r
149                                 // type it is.  I don't know how to deal with this, so for\r
150                                 // now, just skip it.\r
151                                 \r
152                                 msoLogger.warn (MessageEnum.BPMN_VARIABLE_NULL, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError, atomicInputVariable);\r
153                                 \r
154                                 continue;\r
155                         }\r
156 \r
157                         // There might be a better way to do this...\r
158                         if (value instanceof BigDecimal) {\r
159                                 evaluator.setExternalVariable(variable,\r
160                                         new XdmAtomicValue((BigDecimal) value));\r
161                         } else if (value instanceof Boolean) {\r
162                                 evaluator.setExternalVariable(variable,\r
163                                         new XdmAtomicValue((Boolean) value));\r
164                         } else if (value instanceof Double) {\r
165                                 evaluator.setExternalVariable(variable,\r
166                                         new XdmAtomicValue((Double) value));\r
167                         } else if (value instanceof Float) {\r
168                                 evaluator.setExternalVariable(variable,\r
169                                         new XdmAtomicValue((Float) value));\r
170                         } else if (value instanceof Long) {\r
171                                 evaluator.setExternalVariable(variable,\r
172                                         new XdmAtomicValue((Long) value));\r
173                         } else if (value instanceof String) {\r
174                                 evaluator.setExternalVariable(variable,\r
175                                         new XdmAtomicValue((String) value));\r
176                         } else if (value instanceof URI) {\r
177                                 evaluator.setExternalVariable(variable,\r
178                                         new XdmAtomicValue((URI) value));\r
179                         } else {\r
180                                 throw new BadInjectedFieldException(\r
181                                         "atomicInputVariables", getTaskName(),\r
182                                         "'" + atomicInputVariable + "' type is not supported: "\r
183                                                 + value.getClass());\r
184                         }\r
185                 }\r
186 \r
187                 // Evaluate the query and collect the output.\r
188                 StringBuilder output = new StringBuilder();\r
189                 for(XdmItem item : evaluator) {\r
190                         \r
191                         if (msoLogger.isDebugEnabled()) {\r
192                                 msoLogger.debug("XQuery result item = " + item);\r
193                         }\r
194 \r
195                         output.append(item.toString());\r
196                 }\r
197 \r
198                 // Set the output variable.\r
199                 execution.setVariable(theOutputVariable, output.toString());\r
200 \r
201                 if (msoLogger.isDebugEnabled()) {\r
202                         msoLogger.debug("Done Executing " + getTaskName());\r
203                 }\r
204         }\r
205         \r
206         /**\r
207          * Compiles an XQuery script contained in a resource (file).\r
208          * @param compiler the XQueryCompiler\r
209          * @param resource the resource path\r
210          * @return an XQueryExecutable\r
211          * @throws Exception on error\r
212          */\r
213         private XQueryExecutable compile(XQueryCompiler compiler, String resource)\r
214                         throws Exception {\r
215                 try(InputStream xqStream = getClass().getResourceAsStream(resource)) {\r
216 \r
217                         if (xqStream == null) {\r
218                                 throw new IOException("Resource not found: " + resource);\r
219                         }\r
220 \r
221                         return compiler.compile(xqStream);\r
222                 }\r
223         }\r
224 }