Reduce sonar code smells
[yangtools.git] / yang / yang-model-validator / src / main / java / org / opendaylight / yangtools / yang / validator / Main.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.validator;
9
10 import ch.qos.logback.classic.Level;
11 import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
12 import ch.qos.logback.classic.spi.ILoggingEvent;
13 import ch.qos.logback.core.FileAppender;
14 import com.google.common.base.Stopwatch;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Set;
21 import org.apache.commons.cli.CommandLine;
22 import org.apache.commons.cli.CommandLineParser;
23 import org.apache.commons.cli.DefaultParser;
24 import org.apache.commons.cli.HelpFormatter;
25 import org.apache.commons.cli.Option;
26 import org.apache.commons.cli.OptionGroup;
27 import org.apache.commons.cli.Options;
28 import org.apache.commons.cli.ParseException;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * Main class of Yang parser system test.
36  *
37  * <p>
38  * yang-system-test [-f features] [-h help] [-p path] [-v verbose] yangFiles...
39  *  -f,--features &lt;arg&gt;   features is a string in the form
40  *                        [feature(,feature)*] and feature is a string in the form
41  *                        [($namespace?revision=$revision)$local_name].
42  *                        This option is used to prune the data model by removing
43  *                        all nodes that are defined with a "if-feature".
44  *  -h,--help             print help message and exit.
45  *  -p,--path &lt;arg&gt;       path is a colon (:) separated list of directories
46  *                        to search for yang modules.
47  *  -r, --recursive       recursive search of directories specified by -p option
48  *  -v, --verbose         shows details about the results of test running.
49  *  -o, --output          path to output file for logs. Output file will be overwritten.
50  *  -m, --module-name     validate yang by module name.
51  */
52 @SuppressWarnings({"checkstyle:LoggerMustBeSlf4j", "checkstyle:LoggerFactoryClassParameter"})
53 public final class Main {
54     private static final Logger LOG = LoggerFactory.getLogger(Main.class);
55     private static final ch.qos.logback.classic.Logger LOG_ROOT =
56             (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
57     private static final int MB = 1024 * 1024;
58
59     private static final Option FEATURE = new Option("f", "features", true,
60         "features is a string in the form [feature(,feature)*] and feature is a string in the form "
61                 + "[($namespace?revision=$revision)$local_name]. This option is used to prune the data model "
62                 + "by removing all nodes that are defined with a \"if-feature\".");
63
64     private static final Option HELP = new Option("h", "help", false, "print help message and exit.");
65     private static final Option MODULE_NAME = new Option("m", "module-name", true,
66             "validate yang model by module name.");
67     private static final Option OUTPUT = new Option("o", "output", true,
68             "path to output file for logs. Output file will be overwritten");
69     private static final Option PATH = new Option("p", "path", true,
70             "path is a colon (:) separated list of directories to search for yang modules.");
71     private static final Option RECURSIVE = new Option("r", "recursive", false,
72             "recursive search of directories specified by -p option.");
73
74     private static final Option DEBUG = new Option("d", "debug", false, "add debug output");
75     private static final Option QUIET = new Option("q", "quiet", false, "completely suppress output.");
76     private static final Option VERBOSE = new Option("v", "verbose", false,
77         "shows details about the results of test running.");
78
79     private Main() {
80         // Hidden on purpose
81     }
82
83     private static Options createOptions() {
84         final Options options = new Options();
85         options.addOption(HELP);
86         options.addOption(PATH);
87         options.addOption(RECURSIVE);
88
89         final OptionGroup verbosity = new OptionGroup();
90         verbosity.addOption(DEBUG);
91         verbosity.addOption(QUIET);
92         verbosity.addOption(VERBOSE);
93         options.addOptionGroup(verbosity);
94
95         options.addOption(OUTPUT);
96         options.addOption(MODULE_NAME);
97         options.addOption(FEATURE);
98         return options;
99     }
100
101     public static void main(final String[] args) {
102         final HelpFormatter formatter = new HelpFormatter();
103         final Options options = createOptions();
104         final CommandLine arguments = parseArguments(args, options, formatter);
105
106         if (arguments.hasOption(HELP.getLongOpt())) {
107             printHelp(options, formatter);
108             return;
109         }
110
111         final String[] outputValues = arguments.getOptionValues(OUTPUT.getLongOpt());
112         if (outputValues != null) {
113             setOutput(outputValues);
114         }
115
116         LOG_ROOT.setLevel(Level.WARN);
117         if (arguments.hasOption(DEBUG.getLongOpt())) {
118             LOG_ROOT.setLevel(Level.DEBUG);
119         } else if (arguments.hasOption(VERBOSE.getLongOpt())) {
120             LOG_ROOT.setLevel(Level.INFO);
121         } else if (arguments.hasOption(QUIET.getLongOpt())) {
122             LOG_ROOT.detachAndStopAllAppenders();
123         }
124
125         final List<String> yangLibDirs = initYangDirsPath(arguments);
126         final List<String> yangFiles = new ArrayList<>();
127         final String[] moduleNameValues = arguments.getOptionValues(MODULE_NAME.getLongOpt());
128         if (moduleNameValues != null) {
129             yangFiles.addAll(Arrays.asList(moduleNameValues));
130         }
131         yangFiles.addAll(Arrays.asList(arguments.getArgs()));
132
133         final Set<QName> supportedFeatures = initSupportedFeatures(arguments);
134
135         runSystemTest(yangLibDirs, yangFiles, supportedFeatures, arguments.hasOption("recursive"));
136
137         LOG_ROOT.getLoggerContext().reset();
138     }
139
140     private static void setOutput(final String... paths) {
141         LOG_ROOT.getLoggerContext().reset();
142
143         final PatternLayoutEncoder encoder = new PatternLayoutEncoder();
144         encoder.setPattern("%date %level [%thread] [%file:%line] %msg%n");
145         encoder.setContext(LOG_ROOT.getLoggerContext());
146         encoder.start();
147
148         for (final String path : paths) {
149             // create FileAppender
150             final FileAppender<ILoggingEvent> logfileOut = new FileAppender<>();
151             logfileOut.setAppend(false);
152             logfileOut.setFile(path);
153             logfileOut.setContext(LOG_ROOT.getLoggerContext());
154             logfileOut.setEncoder(encoder);
155             logfileOut.start();
156
157             // attach the rolling file appender to the root logger
158             LOG_ROOT.addAppender(logfileOut);
159         }
160     }
161
162     @SuppressWarnings("checkstyle:illegalCatch")
163     private static void runSystemTest(final List<String> yangLibDirs, final List<String> yangFiles,
164             final Set<QName> supportedFeatures, final boolean recursiveSearch) {
165         LOG.info("Yang model dirs: {} ", yangLibDirs);
166         LOG.info("Yang model files: {} ", yangFiles);
167         LOG.info("Supported features: {} ", supportedFeatures);
168
169         SchemaContext context = null;
170
171         printMemoryInfo("start");
172         final Stopwatch stopWatch = Stopwatch.createStarted();
173
174         try {
175             context = SystemTestUtils.parseYangSources(yangLibDirs, yangFiles, supportedFeatures, recursiveSearch);
176         } catch (final Exception e) {
177             LOG.error("Failed to create SchemaContext.", e);
178             System.exit(1);
179         }
180
181         stopWatch.stop();
182         LOG.info("Elapsed time: {}", stopWatch);
183         printMemoryInfo("end");
184         LOG.info("SchemaContext resolved Successfully. {}", context);
185         Runtime.getRuntime().gc();
186         printMemoryInfo("after gc");
187     }
188
189     private static List<String> initYangDirsPath(final CommandLine arguments) {
190         final List<String> yangDirs = new ArrayList<>();
191         if (arguments.hasOption("path")) {
192             for (final String pathArg : arguments.getOptionValues("path")) {
193                 yangDirs.addAll(Arrays.asList(pathArg.split(":")));
194             }
195         }
196         return yangDirs;
197     }
198
199     private static Set<QName> initSupportedFeatures(final CommandLine arguments) {
200         Set<QName> supportedFeatures = null;
201         if (arguments.hasOption("features")) {
202             supportedFeatures = new HashSet<>();
203             for (final String pathArg : arguments.getOptionValues("features")) {
204                 supportedFeatures.addAll(createQNames(pathArg.split(",")));
205             }
206         }
207         return supportedFeatures;
208     }
209
210     private static Collection<? extends QName> createQNames(final String[] featuresArg) {
211         final Set<QName> qnames = new HashSet<>();
212         for (final String featureStr : featuresArg) {
213             qnames.add(QName.create(featureStr));
214         }
215
216         return qnames;
217     }
218
219     private static CommandLine parseArguments(final String[] args, final Options options,
220             final HelpFormatter formatter) {
221         final CommandLineParser parser = new DefaultParser();
222
223         CommandLine cmd = null;
224         try {
225             cmd = parser.parse(options, args);
226         } catch (final ParseException e) {
227             LOG.error("Failed to parse command line options.", e);
228             printHelp(options, formatter);
229             System.exit(1);
230         }
231
232         return cmd;
233     }
234
235     private static void printHelp(final Options options, final HelpFormatter formatter) {
236         formatter.printHelp("yang-system-test [OPTION...] YANG-FILE...", options);
237     }
238
239     private static void printMemoryInfo(final String info) {
240         LOG.info("Memory INFO [{}]: free {}MB, used {}MB, total {}MB, max {}MB", info,
241             Runtime.getRuntime().freeMemory() / MB,
242             (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / MB,
243             Runtime.getRuntime().totalMemory() / MB, Runtime.getRuntime().maxMemory() / MB);
244     }
245 }