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