Merge "Remove unnecessary parentheses"
[ccsdk/sli/core.git] / sli / common / src / main / java / org / onap / ccsdk / sli / core / sli / SvcLogicParser.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : CCSDK
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights
6  *                                              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.onap.ccsdk.sli.core.sli;
23
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.xml.sax.Attributes;
27 import org.xml.sax.Locator;
28 import org.xml.sax.SAXException;
29 import org.xml.sax.SAXNotRecognizedException;
30 import org.xml.sax.SAXParseException;
31 import org.xml.sax.helpers.DefaultHandler;
32
33 import javax.xml.XMLConstants;
34 import javax.xml.parsers.SAXParser;
35 import javax.xml.parsers.SAXParserFactory;
36 import javax.xml.validation.Schema;
37 import javax.xml.validation.SchemaFactory;
38 import java.io.File;
39 import java.io.InputStream;
40 import java.net.URL;
41 import java.util.LinkedList;
42
43 /**
44  * @author dt5972
45  *
46  */
47 public class SvcLogicParser {
48
49     private SvcLogicStore store = null;
50     static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
51     static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
52     static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
53     static final String JAXP_DYNAMIC_VALIDATION = "http://apache.org/xml/features/validation/dynamic";
54     static final String JAXP_SCHEMA_VALIDATION = "http://apache.org/xml/features/validation/schema";
55
56     private static final String LOAD_MESSAGE = "Getting SvcLogicGraph from database - {}";
57     private static final String LOAD_ERROR_MESSAGE = "SvcLogicGraph not found - {}";
58     private static final String ACTIVATION_ERROR_MESSAGE = "Could not activate SvcLogicGraph - {}";
59     private static final String PRINT_ERROR_MESSAGE = "Could not print SvcLogicGraph - {}";
60     private static final String SVC_LOGIC_STORE_ERROR = "Could not get service logic store";
61
62     private static final Logger LOGGER = LoggerFactory.getLogger(SvcLogicParser.class);
63     private static final String SLI_VALIDATING_PARSER = "org.onap.ccsdk.sli.parser.validate";
64     private static final String SVCLOGIC_XSD = "/svclogic.xsd";
65
66     private class SvcLogicHandler extends DefaultHandler {
67     private Locator locator = null;
68     private String module = null;
69     private String version = null;
70     private LinkedList<SvcLogicGraph> graphs = null;
71     private SvcLogicGraph curGraph = null;
72     private SvcLogicNode curNode = null;
73     private LinkedList<SvcLogicNode> nodeStack = null;
74     private int curNodeId = 0;
75     private String outcomeValue = null;
76     private LinkedList<String> outcomeStack = null;
77     private SvcLogicStore svcLogicStore = null;
78
79     public SvcLogicHandler(LinkedList<SvcLogicGraph> graphs, SvcLogicStore store) {
80         this.graphs = graphs;
81         this.curNode = null;
82         this.nodeStack = new LinkedList<>();
83         this.outcomeStack = new LinkedList<>();
84         this.curNodeId = 1;
85         this.outcomeValue = null;
86         this.svcLogicStore = store;
87
88     }
89
90     @Override
91         public void setDocumentLocator(Locator locator) {
92         this.locator = locator;
93     }
94
95     @Override
96     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
97
98         // Handle service-logic (graph) tag
99         if ("service-logic".equalsIgnoreCase(qName)) {
100
101             module = attributes.getValue("module");
102             if (module == null || module.length() == 0) {
103                 throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
104                     + "Missing 'module' attribute from service-logic tag");
105             }
106
107             version = attributes.getValue("version");
108             if (version == null || version.length() == 0) {
109                 throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
110                     + "Missing 'version' attribute from service-logic tag");
111             }
112
113             return;
114         }
115
116         if ("method".equalsIgnoreCase(qName)) {
117
118             if (curGraph != null) {
119                 throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
120                     + "Cannot nest module tags");
121             }
122
123             curGraph = new SvcLogicGraph();
124             curGraph.setModule(module);
125             curGraph.setVersion(version);
126             this.curNodeId = 1;
127
128             String attrValue = attributes.getValue("rpc");
129             if (attrValue == null || attrValue.length() == 0) {
130                 throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
131                     + "Missing 'rpc' attribute for method tag");
132             }
133             curGraph.setRpc(attrValue);
134
135             attrValue = attributes.getValue("mode");
136             if (attrValue == null || attrValue.length() == 0) {
137                 throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
138                     + "Missing 'mode' attribute for method tag");
139             }
140             curGraph.setMode(attrValue);
141
142             return;
143         }
144
145         // Handle outcome (edge) tag
146         if ("outcome".equalsIgnoreCase(qName)) {
147             String refValue = attributes.getValue("ref");
148
149             if (refValue != null) {
150                 SvcLogicNode refNode = curGraph.getNamedNode(refValue);
151
152                 if (refNode != null) {
153                     try {
154                         curNode.addOutcome(attributes.getValue("value"), refNode);
155                     } catch (SvcLogicException e) {
156                         throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
157                             + "Cannot add outcome", e);
158                     }
159                 } else {
160                     throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
161                         + "ref to unknown node " + refValue);
162                 }
163                 return;
164             }
165
166             if (outcomeValue != null) {
167                 outcomeStack.push(outcomeValue);
168             }
169             outcomeValue = attributes.getValue("value");
170
171             return;
172         }
173
174         // Handle parameter tag
175         if ("parameter".equalsIgnoreCase(qName)) {
176             String parmName = attributes.getValue("name");
177             String parmValue = attributes.getValue("value");
178
179             if (parmName != null && parmName.length() > 0 && parmValue != null) {
180                 try {
181
182                     curNode.mapParameter(parmName, parmValue);
183                 } catch (Exception e) {
184                     throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
185                         + " cannot set parameter " + parmName + " to " + parmValue + " [" + e.getMessage() + "]");
186                 }
187             }
188
189             return;
190         }
191
192         // Handle node tags
193         String nodeName = attributes.getValue("name");
194         SvcLogicNode thisNode;
195
196         try {
197             if (!svcLogicStore.isValidNodeType(qName)) {
198                 throw new SAXNotRecognizedException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber()
199                     + " " + "Unknown tag " + qName);
200             }
201         } catch (Exception e) {
202             throw new SAXNotRecognizedException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber()
203                 + " " + "Cannot validate node type " + qName);
204         }
205
206         try {
207             if (nodeName != null && nodeName.length() > 0) {
208                 thisNode = new SvcLogicNode(curNodeId++, qName, nodeName, curGraph);
209             } else {
210                 thisNode = new SvcLogicNode(curNodeId++, qName, curGraph);
211             }
212
213             if (curGraph.getRootNode() == null) {
214                 curGraph.setRootNode(thisNode);
215             }
216         } catch (SvcLogicException e) {
217             throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
218                 + e.getMessage());
219
220         }
221
222         int numAttributes = attributes.getLength();
223
224         for (int i = 0; i < numAttributes; i++) {
225         String attrName = attributes.getQName(i);
226         if (!"name".equalsIgnoreCase(attrName)) {
227             try {
228
229             String attrValueStr = attributes.getValue(i);
230             SvcLogicExpression attrValue;
231             if (attrValueStr.trim().startsWith("`")) {
232                 int lastParen = attrValueStr.lastIndexOf('`');
233                 String evalExpr = attrValueStr.trim().substring(1, lastParen);
234                 attrValue = SvcLogicExpressionFactory.parse(evalExpr);
235
236             } else {
237                 if (Character.isDigit(attrValueStr.charAt(0))) {
238                 attrValue = new SvcLogicAtom("NUMBER", attrValueStr);
239                 } else {
240                 attrValue = new SvcLogicAtom("STRING", attrValueStr);
241                 }
242             }
243             thisNode.setAttribute(attrName, attrValue);
244             } catch (Exception e) {
245             throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
246                 + "Cannot set attribute " + attrName, e);
247             }
248         }
249         }
250
251         if (curNode != null) {
252             try {
253                 if ("block".equalsIgnoreCase(curNode.getNodeType()) || "for".equalsIgnoreCase(curNode.getNodeType())
254                     || "while".equalsIgnoreCase(curNode.getNodeType())) {
255                     curNode.addOutcome(Integer.toString(curNode.getNumOutcomes() + 1), thisNode);
256                 } else {
257                     if (outcomeValue == null) {
258                         throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
259                             + curNode.getNodeType() + " node expects outcome, instead found " + thisNode.getNodeType());
260                     }
261                     curNode.addOutcome(outcomeValue, thisNode);
262                 }
263             } catch (SvcLogicException e) {
264                 throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
265                     + e.getMessage());
266             }
267             nodeStack.push(curNode);
268         }
269         curNode = thisNode;
270
271     }
272
273     @Override
274     public void endElement(String uri, String localName, String qName) throws SAXException {
275
276         // Handle close of service-logic tag
277         if ("service-logic".equalsIgnoreCase(qName)) {
278             // Nothing more to do
279             return;
280         }
281
282         // Handle close of method tag
283         if ("method".equalsIgnoreCase(qName)) {
284             graphs.add(curGraph);
285             curGraph = null;
286             return;
287         }
288
289         // Handle close of outcome tag
290         if ("outcome".equalsIgnoreCase(qName)) {
291             // Finished this outcome - pop the outcome stack
292             if (outcomeStack.isEmpty()) {
293                 outcomeValue = null;
294             } else {
295                 outcomeValue = outcomeStack.pop();
296             }
297             return;
298         }
299
300         // Handle close of parameter tag - do nothing
301         if ("parameter".equalsIgnoreCase(qName)) {
302             return;
303         }
304
305         // Handle close of a node tag
306         if (nodeStack.isEmpty()) {
307             curNode = null;
308         } else {
309             curNode = nodeStack.pop();
310         }
311     }
312
313     @Override
314     public void error(SAXParseException arg0) throws SAXException {
315         throw new SAXException("line " + locator.getLineNumber() + ":" + locator.getColumnNumber() + " "
316             + arg0.getMessage());
317     }
318
319     }
320
321     public SvcLogicParser(SvcLogicStore store) {
322         this.store = store;
323     }
324
325     public SvcLogicParser(String propFile) {
326
327         try {
328             this.store = SvcLogicStoreFactory.getSvcLogicStore(propFile);
329         } catch (Exception e) {
330             LOGGER.error(SVC_LOGIC_STORE_ERROR, e);
331
332         }
333
334     }
335
336     public SvcLogicParser(InputStream propStr) {
337
338         try {
339             this.store = SvcLogicStoreFactory.getSvcLogicStore(propStr);
340         } catch (Exception e) {
341             LOGGER.error(SVC_LOGIC_STORE_ERROR, e);
342
343         }
344
345     }
346
347     public LinkedList<SvcLogicGraph> parse(String fileName) throws SvcLogicException {
348         LinkedList<SvcLogicGraph> graphs;
349
350         URL xsdUrl = null;
351         Schema schema = null;
352         String validateSchema = System.getProperty(SLI_VALIDATING_PARSER, "true");
353
354         if ("true".equalsIgnoreCase(validateSchema)) {
355             xsdUrl = getClass().getResource(SVCLOGIC_XSD);
356         }
357
358         if (xsdUrl != null) {
359             try {
360                 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
361                 schema = schemaFactory.newSchema(xsdUrl);
362                 LOGGER.info("Schema path {}", xsdUrl.getPath());
363             } catch (Exception e) {
364                 LOGGER.warn("Could not validate using schema {}", xsdUrl.getPath(), e);
365             }
366         } else {
367             LOGGER.warn("Could not find resource {}", SVCLOGIC_XSD);
368         }
369
370         try {
371             SAXParserFactory factory = SAXParserFactory.newInstance();
372
373             if (schema != null) {
374                 factory.setNamespaceAware(true);
375                 factory.setSchema(schema);
376             }
377             SAXParser saxParser = factory.newSAXParser();
378
379             if (saxParser.isValidating()) {
380                 LOGGER.info("Parser configured to validate XML {}", (xsdUrl != null ? xsdUrl.getPath() : null));
381             }
382
383             graphs = new LinkedList<>();
384
385             saxParser.parse(fileName, new SvcLogicHandler(graphs, store));
386
387         } catch (Exception e) {
388             LOGGER.error("Parsing failed {}", e);
389             String msg = e.getMessage();
390             if (msg != null) {
391                 throw new SvcLogicException("Compiler error: " + fileName + " @ " + msg);
392             } else {
393                 throw new SvcLogicException("Compiler error: " + fileName, e);
394             }
395         }
396
397         return graphs;
398     }
399
400     public static void main(String argv[]) {
401
402         if (argv.length == 0) {
403             SvcLogicParser.usage();
404         }
405
406         if ("load".equalsIgnoreCase(argv[0])) {
407             if (argv.length == 3) {
408                 String xmlfile = argv[1];
409                 String propfile = argv[2];
410
411                 SvcLogicStore store = SvcLogicParser.getStore(propfile);
412                 try {
413                     SvcLogicParser.load(xmlfile, store);
414                 } catch (Exception e) {
415                     LOGGER.error("Load failed {}", e);
416                 }
417             } else {
418                 SvcLogicParser.usage();
419             }
420         } else if ("print".equalsIgnoreCase(argv[0])) {
421             String version = null;
422             String propfile = null;
423
424             switch (argv.length) {
425             case 6:
426                 version = argv[4];
427                 propfile = argv[5];
428             case 5:
429                 if (propfile == null) {
430                     propfile = argv[4];
431                 }
432                 SvcLogicStore store = SvcLogicParser.getStore(propfile);
433                 SvcLogicParser.print(argv[1], argv[2], argv[3], version, store);
434                 break;
435             default:
436                 SvcLogicParser.usage();
437             }
438         } else if ("get-source".equalsIgnoreCase(argv[0])) {
439
440             if (argv.length == 6) {
441                 SvcLogicStore store = SvcLogicParser.getStore(argv[5]);
442                 SvcLogicParser.getSource(argv[1], argv[2], argv[3], argv[4], store);
443             } else {
444                 SvcLogicParser.usage();
445             }
446         } else if ("activate".equalsIgnoreCase(argv[0])) {
447             if (argv.length == 6) {
448             SvcLogicStore store = SvcLogicParser.getStore(argv[5]);
449             SvcLogicParser.activate(argv[1], argv[2], argv[3], argv[4], store);
450             } else {
451             SvcLogicParser.usage();
452             }
453         } else if ("validate".equalsIgnoreCase(argv[0])) {
454             if (argv.length == 3) {
455             String xmlfile = argv[1];
456             String propfile = argv[2];
457
458             System.setProperty(SLI_VALIDATING_PARSER, "true");
459             SvcLogicStore store = SvcLogicParser.getStore(propfile);
460             try {
461                 SvcLogicParser.validate(xmlfile, store);
462             } catch (Exception e) {
463                 LOGGER.error("Validate failed", e);
464             }
465             } else {
466             SvcLogicParser.usage();
467             }
468         }
469
470         System.exit(0);
471     }
472
473     private static SvcLogicStore getStore(String propfile) {
474
475         SvcLogicStore store = null;
476
477         try {
478             store = SvcLogicStoreFactory.getSvcLogicStore(propfile);
479         } catch (Exception e) {
480             LOGGER.error(SVC_LOGIC_STORE_ERROR, e);
481             System.exit(1);
482         }
483
484         return store;
485
486     }
487
488     public static void load(String xmlfile, SvcLogicStore store) throws SvcLogicException {
489         File xmlFile = new File(xmlfile);
490         if (!xmlFile.canRead()) {
491             throw new ConfigurationException("Cannot read xml file (" + xmlfile + ")");
492         }
493
494         SvcLogicParser parser = new SvcLogicParser(store);
495         LinkedList<SvcLogicGraph> graphs;
496         try {
497             LOGGER.info("Loading {}", xmlfile);
498             graphs = parser.parse(xmlfile);
499         } catch (Exception e) {
500             throw new SvcLogicException(e.getMessage(), e);
501         }
502
503         if (graphs == null) {
504             throw new SvcLogicException("Could not parse " + xmlfile);
505         }
506
507         for (SvcLogicGraph graph : graphs) {
508
509             String module = graph.getModule();
510             String rpc = graph.getRpc();
511             String version = graph.getVersion();
512             String mode = graph.getMode();
513             try {
514                 LOGGER.info("Saving SvcLogicGraph to database (module:{},rpc:{},mode:{},version:{})", module, rpc,
515                     mode, version);
516                 store.store(graph);
517             } catch (Exception e) {
518                 throw new SvcLogicException(e.getMessage(), e);
519             }
520
521         }
522
523     }
524
525     public static void validate(String xmlfile, SvcLogicStore store) throws SvcLogicException {
526         File xmlFile = new File(xmlfile);
527         if (!xmlFile.canRead()) {
528             throw new ConfigurationException("Cannot read xml file (" + xmlfile + ")");
529         }
530
531         SvcLogicParser parser = new SvcLogicParser(store);
532         LinkedList<SvcLogicGraph> graphs;
533         try {
534             LOGGER.info("Validating {}", xmlfile);
535             graphs = parser.parse(xmlfile);
536         } catch (Exception e) {
537             throw new SvcLogicException(e.getMessage(), e);
538         }
539
540         if (graphs == null) {
541             throw new SvcLogicException("Could not parse " + xmlfile);
542         } else {
543             LOGGER.info("Compilation successful for {}", xmlfile);
544         }
545
546     }
547
548     private static void print(String module, String rpc, String mode, String version, SvcLogicStore store) {
549         String details = "(module:" + module + ", rpc:" + rpc + ", version:" + version + ", mode:" + mode + ")";
550
551         try {
552             LOGGER.info(LOAD_MESSAGE, details);
553
554             SvcLogicGraph graph = store.fetch(module, rpc, version, mode);
555             if (graph == null) {
556                 LOGGER.error(LOAD_ERROR_MESSAGE, details);
557                 System.exit(1);
558             }
559             graph.printAsGv(System.out);
560         } catch (Exception e) {
561             LOGGER.error(PRINT_ERROR_MESSAGE, details, e);
562             System.exit(1);
563         }
564
565     }
566
567     private static void getSource(String module, String rpc, String mode, String version, SvcLogicStore store) {
568         String details = "(module:" + module + ", rpc:" + rpc + ", version:" + version + ", mode:" + mode + ")";
569
570         try {
571             LOGGER.info(LOAD_MESSAGE, details);
572
573             SvcLogicGraph graph = store.fetch(module, rpc, version, mode);
574             if (graph == null) {
575                 LOGGER.error(LOAD_ERROR_MESSAGE, details);
576                 System.exit(1);
577             }
578             graph.printAsXml(System.out);
579         } catch (Exception e) {
580             LOGGER.error(PRINT_ERROR_MESSAGE, details, e);
581             System.exit(1);
582         }
583
584     }
585
586     private static void activate(String module, String rpc, String version, String mode, SvcLogicStore store) {
587         String details = "(module:" + module + ", rpc:" + rpc + ", version:" + version + ", mode:" + mode + ")";
588
589         try {
590             LOGGER.info(LOAD_MESSAGE, details);
591
592             SvcLogicGraph graph = store.fetch(module, rpc, version, mode);
593             if (graph == null) {
594                 LOGGER.error(LOAD_ERROR_MESSAGE, details);
595                 System.exit(1);
596             }
597             store.activate(graph);
598         } catch (Exception e) {
599             LOGGER.error(ACTIVATION_ERROR_MESSAGE, details, e);
600             System.exit(1);
601         }
602
603     }
604
605     private static void usage() {
606         System.err.println("Usage: SvcLogicParser load <xml-file> <prop-file>");
607         System.err.println(" OR    SvcLogicParser print <module> <rpc> <mode> [<version>] <prop-file>");
608         System.err.println(" OR    SvcLogicParser get-source <module> <rpc> <mode> <version> <prop-file>");
609         System.err.println(" OR    SvcLogicParser activate <module> <rpc> <version> <mode>");
610         System.exit(1);
611     }
612
613 }