2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.yang2sources.plugin;
10 import java.io.Closeable;
12 import java.io.FilenameFilter;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Enumeration;
19 import java.util.HashSet;
20 import java.util.List;
23 import java.util.zip.ZipEntry;
24 import java.util.zip.ZipFile;
26 import org.apache.maven.model.Resource;
27 import org.apache.maven.plugin.MojoExecutionException;
28 import org.apache.maven.plugin.MojoFailureException;
29 import org.apache.maven.plugin.logging.Log;
30 import org.apache.maven.project.MavenProject;
31 import org.codehaus.plexus.util.FileUtils;
32 import org.opendaylight.controller.yang.model.api.Module;
33 import org.opendaylight.controller.yang.model.api.SchemaContext;
34 import org.opendaylight.controller.yang.parser.impl.YangParserImpl;
35 import org.opendaylight.controller.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
36 import org.opendaylight.controller.yang2sources.plugin.Util.NamedFileInputStream;
37 import org.opendaylight.controller.yang2sources.spi.CodeGenerator;
39 import com.google.common.collect.Maps;
41 class YangToSourcesProcessor {
42 private static final String LOG_PREFIX = "yang-to-sources:";
43 private static final String META_INF_YANG_STRING = "META-INF"
44 + File.separator + "yang";
45 private static final File META_INF_YANG_DIR = new File(META_INF_YANG_STRING);
47 private final Log log;
48 private final File yangFilesRootDir;
49 private final List<CodeGeneratorArg> codeGenerators;
50 private final MavenProject project;
51 private final boolean inspectDependencies;
53 YangToSourcesProcessor(Log log, File yangFilesRootDir,
54 List<CodeGeneratorArg> codeGenerators, MavenProject project,
55 boolean inspectDependencies) {
56 this.log = checkNotNull(log, "log");
57 this.yangFilesRootDir = checkNotNull(yangFilesRootDir,
59 this.codeGenerators = Collections.unmodifiableList(checkNotNull(
60 codeGenerators, "codeGenerators"));
61 this.project = checkNotNull(project, "project");
62 this.inspectDependencies = inspectDependencies;
65 private static <T> T checkNotNull(T obj, String paramName) {
67 throw new NullPointerException("Parameter '" + paramName
72 public void execute() throws MojoExecutionException, MojoFailureException {
73 ContextHolder context = processYang();
74 generateSources(context);
78 private ContextHolder processYang() throws MojoExecutionException {
79 YangParserImpl parser = new YangParserImpl();
80 List<Closeable> closeables = new ArrayList<>();
81 log.info(Util.message("Inspecting %s", LOG_PREFIX, yangFilesRootDir));
83 List<InputStream> yangsInProject = Util
84 .listFilesAsStream(yangFilesRootDir);
85 List<InputStream> all = new ArrayList<>(yangsInProject);
86 closeables.addAll(yangsInProject);
87 Map<InputStream, Module> allYangModules;
88 Set<Module> projectYangModules;
90 if (inspectDependencies) {
91 YangsInZipsResult dependentYangResult = findYangFilesInDependenciesAsStream();
92 Closeable dependentYangResult1 = dependentYangResult;
93 closeables.add(dependentYangResult1);
94 all.addAll(dependentYangResult.yangStreams);
97 allYangModules = parser.parseYangModelsFromStreamsMapped(all);
99 projectYangModules = new HashSet<>();
100 for (InputStream inProject : yangsInProject) {
101 projectYangModules.add(allYangModules.get(inProject));
105 for (AutoCloseable closeable : closeables) {
110 Set<Module> parsedAllYangModules = new HashSet<>(
111 allYangModules.values());
112 SchemaContext resolveSchemaContext = parser
113 .resolveSchemaContext(parsedAllYangModules);
114 log.info(Util.message("%s files parsed from %s", LOG_PREFIX,
115 Util.YANG_SUFFIX.toUpperCase(), yangsInProject));
116 return new ContextHolder(resolveSchemaContext, projectYangModules);
118 // MojoExecutionException is thrown since execution cannot continue
119 } catch (Exception e) {
120 String message = Util.message("Unable to parse %s files from %s",
121 LOG_PREFIX, Util.YANG_SUFFIX, yangFilesRootDir);
122 log.error(message, e);
123 throw new MojoExecutionException(message, e);
127 private void addYangsToMETA_INF() throws MojoFailureException {
128 Resource res = new Resource();
130 File targetYangDir = new File(project.getBasedir(), "target"
131 + File.separator + "yang");
132 res.setDirectory(targetYangDir.getPath());
134 res.setTargetPath(META_INF_YANG_DIR.getPath());
136 FileUtils.copyDirectory(yangFilesRootDir, targetYangDir);
137 } catch (IOException e) {
138 throw new MojoFailureException(e.getMessage(), e);
140 project.addResource(res);
144 * Call generate on every generator from plugin configuration
146 private void generateSources(ContextHolder context)
147 throws MojoFailureException {
148 if (codeGenerators.size() == 0) {
149 log.warn(Util.message("No code generators provided", LOG_PREFIX));
153 Map<String, String> thrown = Maps.newHashMap();
154 for (CodeGeneratorArg codeGenerator : codeGenerators) {
156 generateSourcesWithOneGenerator(context, codeGenerator);
157 } catch (Exception e) {
158 // try other generators, exception will be thrown after
159 log.error(Util.message(
160 "Unable to generate sources with %s generator",
161 LOG_PREFIX, codeGenerator.getCodeGeneratorClass()), e);
162 thrown.put(codeGenerator.getCodeGeneratorClass(), e.getClass()
163 .getCanonicalName());
167 if (!thrown.isEmpty()) {
168 String message = Util
170 "One or more code generators failed, including failed list(generatorClass=exception) %s",
171 LOG_PREFIX, thrown.toString());
173 throw new MojoFailureException(message);
178 * Instantiate generator from class and call required method
180 private void generateSourcesWithOneGenerator(ContextHolder context,
181 CodeGeneratorArg codeGeneratorCfg) throws ClassNotFoundException,
182 InstantiationException, IllegalAccessException, IOException {
184 codeGeneratorCfg.check();
186 CodeGenerator g = Util.getInstance(
187 codeGeneratorCfg.getCodeGeneratorClass(), CodeGenerator.class);
188 log.info(Util.message("Code generator instantiated from %s",
189 LOG_PREFIX, codeGeneratorCfg.getCodeGeneratorClass()));
191 File outputDir = codeGeneratorCfg.getOutputBaseDir(project);
193 log.info(Util.message("Sources will be generated to %s", LOG_PREFIX,
195 log.info(Util.message("Project root dir is %s", LOG_PREFIX,
196 project.getBasedir()));
197 log.info(Util.message(
198 "Additional configuration picked up for : %s: %s", LOG_PREFIX,
199 codeGeneratorCfg.getCodeGeneratorClass(),
200 codeGeneratorCfg.getAdditionalConfiguration()));
201 project.addCompileSourceRoot(outputDir.getAbsolutePath());
203 g.setAdditionalConfig(codeGeneratorCfg.getAdditionalConfiguration());
204 Collection<File> generated = g.generateSources(context.getContext(),
205 outputDir, context.getYangModules(), project.getBasedir());
206 log.info(Util.message("Sources generated by %s: %s", LOG_PREFIX,
207 codeGeneratorCfg.getCodeGeneratorClass(), generated));
210 private class YangsInZipsResult implements Closeable {
211 private final List<InputStream> yangStreams;
212 private final List<Closeable> zipInputStreams;
214 private YangsInZipsResult(List<InputStream> yangStreams,
215 List<Closeable> zipInputStreams) {
216 this.yangStreams = yangStreams;
217 this.zipInputStreams = zipInputStreams;
221 public void close() throws IOException {
222 for (InputStream is : yangStreams) {
225 for (Closeable is : zipInputStreams) {
231 private YangsInZipsResult findYangFilesInDependenciesAsStream()
232 throws MojoFailureException {
233 List<InputStream> yangsFromDependencies = new ArrayList<>();
234 List<Closeable> zips = new ArrayList<>();
236 List<File> filesOnCp = Util.getClassPath(project);
237 log.info(Util.message(
238 "Searching for yang files in following dependencies: %s",
239 LOG_PREFIX, filesOnCp));
241 for (File file : filesOnCp) {
242 List<String> foundFilesForReporting = new ArrayList<>();
243 // is it jar file or directory?
244 if (file.isDirectory()) {
245 File yangDir = new File(file, META_INF_YANG_STRING);
246 if (yangDir.exists() && yangDir.isDirectory()) {
247 File[] yangFiles = yangDir
248 .listFiles(new FilenameFilter() {
250 public boolean accept(File dir, String name) {
251 return name.endsWith(".yang")
252 && new File(dir, name).isFile();
255 for (File yangFile : yangFiles) {
256 yangsFromDependencies.add(new NamedFileInputStream(
262 ZipFile zip = new ZipFile(file);
265 Enumeration<? extends ZipEntry> entries = zip.entries();
266 while (entries.hasMoreElements()) {
267 ZipEntry entry = entries.nextElement();
268 String entryName = entry.getName();
270 if (entryName.startsWith(META_INF_YANG_STRING)) {
271 if (entry.isDirectory() == false
272 && entryName.endsWith(".yang")) {
273 foundFilesForReporting.add(entryName);
274 // This will be closed after all strams are
276 InputStream entryStream = zip
277 .getInputStream(entry);
278 yangsFromDependencies.add(entryStream);
283 if (foundFilesForReporting.size() > 0) {
284 log.info(Util.message("Found %d yang files in %s: %s",
285 LOG_PREFIX, foundFilesForReporting.size(), file,
286 foundFilesForReporting));
290 } catch (Exception e) {
291 throw new MojoFailureException(e.getMessage(), e);
293 return new YangsInZipsResult(yangsFromDependencies, zips);
296 private class ContextHolder {
297 private final SchemaContext context;
298 private final Set<Module> yangModules;
300 private ContextHolder(SchemaContext context, Set<Module> yangModules) {
301 this.context = context;
302 this.yangModules = yangModules;
305 public SchemaContext getContext() {
309 public Set<Module> getYangModules() {