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.FileInputStream;
13 import java.io.FileOutputStream;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Enumeration;
19 import java.util.List;
22 import java.util.zip.ZipEntry;
23 import java.util.zip.ZipFile;
25 import org.apache.commons.io.IOUtils;
26 import org.apache.maven.model.Resource;
27 import org.apache.maven.plugin.AbstractMojo;
28 import org.apache.maven.plugin.MojoExecutionException;
29 import org.apache.maven.plugin.MojoFailureException;
30 import org.apache.maven.plugins.annotations.LifecyclePhase;
31 import org.apache.maven.plugins.annotations.Mojo;
32 import org.apache.maven.plugins.annotations.Parameter;
33 import org.apache.maven.plugins.annotations.ResolutionScope;
34 import org.apache.maven.project.MavenProject;
35 import org.opendaylight.controller.yang.model.api.Module;
36 import org.opendaylight.controller.yang.model.api.SchemaContext;
37 import org.opendaylight.controller.yang.model.parser.api.YangModelParser;
38 import org.opendaylight.controller.yang.model.parser.impl.YangParserImpl;
39 import org.opendaylight.controller.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
40 import org.opendaylight.controller.yang2sources.plugin.ConfigArg.ResourceProviderArg;
41 import org.opendaylight.controller.yang2sources.spi.CodeGenerator;
42 import org.opendaylight.controller.yang2sources.spi.ResourceGenerator;
44 import com.google.common.annotations.VisibleForTesting;
45 import com.google.common.collect.Lists;
46 import com.google.common.collect.Maps;
49 * Generate sources from yang files using user provided set of
50 * {@link CodeGenerator}s. Steps of this process:
52 * <li>List yang files from {@link #yangFilesRootDir}</li>
53 * <li>Process yang files using {@link YangModelParserImpl}</li>
54 * <li>For each {@link CodeGenerator} from {@link #codeGenerators}:</li>
56 * <li>Instantiate using default constructor</li>
57 * <li>Call {@link CodeGenerator#generateSources(SchemaContext, File)}</li>
61 @Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true)
62 public final class YangToSourcesMojo extends AbstractMojo {
63 private static final String LOG_PREFIX = "yang-to-sources:";
64 private static final String INPUT_RESOURCE_DIR = "META-INF/yangs/";
65 private static final String OUTPUT_RESOURCE_DIR = "/target/external-resources/";
68 * Classes implementing {@link CodeGenerator} interface. An instance will be
69 * created out of every class using default constructor. Method
70 * {@link CodeGenerator#generateSources(SchemaContext, File)} will be called
73 @Parameter(required = true)
74 private CodeGeneratorArg[] codeGenerators;
77 * Source directory that will be recursively searched for yang files (ending
80 @Parameter(required = true)
81 private String yangFilesRootDir;
84 * Classes implementing {@link ResourceGenerator} interface. An instance
85 * will be created out of every class using default constructor. Method
86 * {@link ResourceGenerator#generateResourceFiles(Collection, File)} will be
87 * called on every instance.
89 @Parameter(required = true)
90 private ResourceProviderArg[] resourceProviders;
92 @Parameter(property = "project", required = true, readonly = true)
93 protected MavenProject project;
95 private transient final YangModelParser parser;
98 YangToSourcesMojo(ResourceProviderArg[] resourceProviderArgs,
99 CodeGeneratorArg[] codeGeneratorArgs, YangModelParser parser,
100 String yangFilesRootDir) {
102 this.resourceProviders = resourceProviderArgs;
103 this.codeGenerators = codeGeneratorArgs;
104 this.yangFilesRootDir = yangFilesRootDir;
105 this.parser = parser;
108 public YangToSourcesMojo() {
110 parser = new YangParserImpl();
114 public void execute() throws MojoExecutionException, MojoFailureException {
115 SchemaContext context = processYang();
116 generateSources(context);
123 * Generate {@link SchemaContext} with {@link YangModelParserImpl}
125 private SchemaContext processYang() throws MojoExecutionException {
127 Collection<InputStream> yangFiles = Util
128 .listFilesAsStream(yangFilesRootDir);
129 yangFiles.addAll(getFilesFromDependenciesAsStream());
131 if (yangFiles.isEmpty()) {
133 Util.message("No %s file found in %s", LOG_PREFIX,
134 Util.YANG_SUFFIX, yangFilesRootDir));
138 Set<Module> parsedYang = parser
139 .parseYangModelsFromStreams(new ArrayList<InputStream>(
141 SchemaContext resolveSchemaContext = parser
142 .resolveSchemaContext(parsedYang);
144 Util.message("%s files parsed from %s", LOG_PREFIX,
145 Util.YANG_SUFFIX, yangFiles));
146 return resolveSchemaContext;
148 // MojoExecutionException is thrown since execution cannot continue
149 } catch (Exception e) {
150 String message = Util.message("Unable to parse %s files from %s",
151 LOG_PREFIX, Util.YANG_SUFFIX, yangFilesRootDir);
152 getLog().error(message, e);
153 throw new MojoExecutionException(message, e);
157 private void generateResources() throws MojoExecutionException,
158 MojoFailureException {
159 if (resourceProviders.length == 0) {
161 Util.message("No resource provider classes provided",
166 Resource res = new Resource();
167 String baseDirName = project.getBasedir().getAbsolutePath();
168 res.setDirectory(baseDirName + OUTPUT_RESOURCE_DIR);
169 res.setTargetPath(INPUT_RESOURCE_DIR);
170 project.addResource(res);
172 Map<String, String> thrown = Maps.newHashMap();
174 Collection<File> yangFiles = new ArrayList<File>();
176 // load files from yang root
177 yangFiles.addAll(getFilesFromYangRoot());
179 // load files from dependencies
180 yangFiles.addAll(getFilesFromDependencies());
183 for (ResourceProviderArg resourceProvider : resourceProviders) {
185 provideResourcesWithOneProvider(yangFiles, resourceProvider);
186 } catch (Exception e) {
187 // try other generators, exception will be thrown after
190 "Unable to provide resources with %s resource provider",
192 resourceProvider.getResourceProviderClass()), e);
193 thrown.put(resourceProvider.getResourceProviderClass(), e
194 .getClass().getCanonicalName());
198 if (!thrown.isEmpty()) {
199 String message = Util
201 "One or more code resource provider failed, including failed list(resourceProviderClass=exception) %s",
202 LOG_PREFIX, thrown.toString());
203 getLog().error(message);
204 throw new MojoFailureException(message);
208 private Collection<File> getFilesFromYangRoot() {
209 Collection<File> yangFilesLoaded = Util.listFiles(yangFilesRootDir);
210 Collection<File> yangFiles = new ArrayList<File>(yangFilesLoaded);
213 for(File yangFile : yangFilesLoaded) {
214 InputStream is = new FileInputStream(yangFile);
215 yangFiles.add(createFileFromStream(is, project.getBasedir().getAbsolutePath() + OUTPUT_RESOURCE_DIR + yangFile.getName()));
218 } catch(IOException e) {
219 getLog().warn("Exception while loading yang files.", e);
224 private Collection<File> getFilesFromDependencies() {
225 Collection<File> yangFiles = new ArrayList<File>();
228 List<File> filesOnCp = Util.getClassPath(project);
229 List<String> filter = Lists.newArrayList(".yang");
230 for (File file : filesOnCp) {
231 ZipFile zip = new ZipFile(file);
232 Enumeration<? extends ZipEntry> entries = zip.entries();
234 while (entries.hasMoreElements()) {
235 ZipEntry entry = entries.nextElement();
236 String entryName = entry.getName();
237 if (entryName.startsWith(INPUT_RESOURCE_DIR)) {
238 if (entry.isDirectory()) {
241 if (!Util.acceptedFilter(entryName, filter)) {
244 InputStream entryStream = zip.getInputStream(entry);
245 String newEntryName = entryName.substring(INPUT_RESOURCE_DIR.length());
246 File f = createFileFromStream(entryStream, project.getBasedir().getAbsolutePath() + OUTPUT_RESOURCE_DIR + newEntryName);
249 resources.add(entryStream);
255 } catch (Exception e) {
256 getLog().warn("Exception while loading external yang files.", e);
261 private File createFileFromStream(InputStream is, String absoluteName) throws IOException {
262 File f = new File(absoluteName);
264 f.getParentFile().mkdirs();
268 FileOutputStream fos = new FileOutputStream(f);
269 IOUtils.copy(is, fos);
274 * Instantiate provider from class and call required method
276 private void provideResourcesWithOneProvider(Collection<File> yangFiles,
277 ResourceProviderArg resourceProvider)
278 throws ClassNotFoundException, InstantiationException,
279 IllegalAccessException {
281 resourceProvider.check();
283 ResourceGenerator g = Util.getInstance(
284 resourceProvider.getResourceProviderClass(),
285 ResourceGenerator.class);
287 Util.message("Resource provider instantiated from %s",
288 LOG_PREFIX, resourceProvider.getResourceProviderClass()));
290 g.generateResourceFiles(yangFiles, resourceProvider.getOutputBaseDir());
292 Util.message("Resource provider %s call successful",
293 LOG_PREFIX, resourceProvider.getResourceProviderClass()));
297 * Call generate on every generator from plugin configuration
299 private void generateSources(SchemaContext context)
300 throws MojoFailureException {
301 if (codeGenerators.length == 0) {
303 Util.message("No code generators provided", LOG_PREFIX));
307 Map<String, String> thrown = Maps.newHashMap();
308 for (CodeGeneratorArg codeGenerator : codeGenerators) {
310 generateSourcesWithOneGenerator(context, codeGenerator);
311 } catch (Exception e) {
312 // try other generators, exception will be thrown after
315 "Unable to generate sources with %s generator",
317 codeGenerator.getCodeGeneratorClass()), e);
318 thrown.put(codeGenerator.getCodeGeneratorClass(), e.getClass()
319 .getCanonicalName());
323 if (!thrown.isEmpty()) {
324 String message = Util
326 "One or more code generators failed, including failed list(generatorClass=exception) %s",
327 LOG_PREFIX, thrown.toString());
328 getLog().error(message);
329 throw new MojoFailureException(message);
334 * Instantiate generator from class and call required method
336 private void generateSourcesWithOneGenerator(SchemaContext context,
337 CodeGeneratorArg codeGeneratorCfg) throws ClassNotFoundException,
338 InstantiationException, IllegalAccessException, IOException {
340 codeGeneratorCfg.check();
342 CodeGenerator g = Util.getInstance(
343 codeGeneratorCfg.getCodeGeneratorClass(), CodeGenerator.class);
345 Util.message("Code generator instantiated from %s", LOG_PREFIX,
346 codeGeneratorCfg.getCodeGeneratorClass()));
348 File outputDir = codeGeneratorCfg.getOutputBaseDir();
349 if (project != null && outputDir != null) {
350 project.addCompileSourceRoot(outputDir.getPath());
352 Collection<File> generated = g.generateSources(context, outputDir);
354 Util.message("Sources generated by %s: %s", LOG_PREFIX,
355 codeGeneratorCfg.getCodeGeneratorClass(), generated));
359 * Collection of resources which should be closed after use.
361 private final List<Closeable> resources = new ArrayList<Closeable>();
364 * Search for yang files in dependent projects.
366 * @return files found as List of InputStream
368 private List<InputStream> getFilesFromDependenciesAsStream() {
369 final List<InputStream> yangsFromDependencies = new ArrayList<InputStream>();
371 List<File> filesOnCp = Util.getClassPath(project);
373 List<String> filter = Lists.newArrayList(".yang");
374 for (File file : filesOnCp) {
375 ZipFile zip = new ZipFile(file);
376 Enumeration<? extends ZipEntry> entries = zip.entries();
377 while (entries.hasMoreElements()) {
378 ZipEntry entry = entries.nextElement();
379 String entryName = entry.getName();
381 if(entryName.startsWith(INPUT_RESOURCE_DIR)) {
382 if(entry.isDirectory()) {
385 if (!Util.acceptedFilter(entryName, filter)) {
389 InputStream entryStream = zip.getInputStream(entry);
390 yangsFromDependencies.add(entryStream);
391 resources.add(entryStream);
397 } catch (Exception e) {
398 getLog().warn("Exception while searching yangs in dependencies", e);
400 return yangsFromDependencies;
404 * Internal utility method for closing open resources.
406 private void closeResources() {
407 for (Closeable resource : resources) {
410 } catch (IOException e) {
411 getLog().warn("Failed to close resources: "+ resource, e);