X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fsal%2Fyang-prototype%2Fcode-generator%2Fmaven-yang-plugin%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fyang2sources%2Fplugin%2FYangToSourcesMojo.java;h=ab49a54fd1fa809a439ab42cc8c731973227cb54;hp=32ad2abb104c65a9cfe78aec8acb4bb310796961;hb=bd12f1813d9bc6706eec5dca561506d3709c211c;hpb=9cdfa8361e3b4d3e969821aa4de5c4862e22a025 diff --git a/opendaylight/sal/yang-prototype/code-generator/maven-yang-plugin/src/main/java/org/opendaylight/controller/yang2sources/plugin/YangToSourcesMojo.java b/opendaylight/sal/yang-prototype/code-generator/maven-yang-plugin/src/main/java/org/opendaylight/controller/yang2sources/plugin/YangToSourcesMojo.java index 32ad2abb10..ab49a54fd1 100644 --- a/opendaylight/sal/yang-prototype/code-generator/maven-yang-plugin/src/main/java/org/opendaylight/controller/yang2sources/plugin/YangToSourcesMojo.java +++ b/opendaylight/sal/yang-prototype/code-generator/maven-yang-plugin/src/main/java/org/opendaylight/controller/yang2sources/plugin/YangToSourcesMojo.java @@ -7,27 +7,47 @@ */ package org.opendaylight.controller.yang2sources.plugin; +import java.io.Closeable; import java.io.File; -import java.util.Arrays; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.apache.commons.io.IOUtils; +import org.apache.maven.model.Resource; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; import org.opendaylight.controller.yang.model.api.Module; import org.opendaylight.controller.yang.model.api.SchemaContext; import org.opendaylight.controller.yang.model.parser.api.YangModelParser; -import org.opendaylight.controller.yang.model.parser.impl.YangModelParserImpl; +import org.opendaylight.controller.yang.parser.impl.YangParserImpl; import org.opendaylight.controller.yang2sources.plugin.ConfigArg.CodeGeneratorArg; +import org.opendaylight.controller.yang2sources.plugin.ConfigArg.ResourceProviderArg; import org.opendaylight.controller.yang2sources.spi.CodeGenerator; +import org.opendaylight.controller.yang2sources.spi.ResourceGenerator; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.io.Files; /** * Generate sources from yang files using user provided set of @@ -42,16 +62,17 @@ import com.google.common.collect.Maps; * * */ -@Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES) +@Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true) public final class YangToSourcesMojo extends AbstractMojo { - private static final String LOG_PREFIX = "yang-to-sources:"; + private static final String INPUT_RESOURCE_DIR = "META-INF/yang/"; + private static final String OUTPUT_RESOURCE_DIR = "/target/external-resources/"; /** * Classes implementing {@link CodeGenerator} interface. An instance will be - * created out of every class using default constructor. Method - * {@link CodeGenerator#generateSources(SchemaContext, File)} will be called - * on every instance. + * created out of every class using default constructor. Method {@link + * CodeGenerator#generateSources(SchemaContext, File, Set + * yangModulesNames)} will be called on every instance. */ @Parameter(required = true) private CodeGeneratorArg[] codeGenerators; @@ -63,12 +84,26 @@ public final class YangToSourcesMojo extends AbstractMojo { @Parameter(required = true) private String yangFilesRootDir; - private final YangModelParser parser; + /** + * Classes implementing {@link ResourceGenerator} interface. An instance + * will be created out of every class using default constructor. Method + * {@link ResourceGenerator#generateResourceFiles(Collection, File)} will be + * called on every instance. + */ + @Parameter(required = true) + private ResourceProviderArg[] resourceProviders; + + @Parameter(property = "project", required = true, readonly = true) + protected MavenProject project; + + private transient final YangModelParser parser; @VisibleForTesting - YangToSourcesMojo(CodeGeneratorArg[] codeGeneratorArgs, - YangModelParser parser, String yangFilesRootDir) { + YangToSourcesMojo(ResourceProviderArg[] resourceProviderArgs, + CodeGeneratorArg[] codeGeneratorArgs, YangModelParser parser, + String yangFilesRootDir) { super(); + this.resourceProviders = resourceProviderArgs; this.codeGenerators = codeGeneratorArgs; this.yangFilesRootDir = yangFilesRootDir; this.parser = parser; @@ -76,35 +111,55 @@ public final class YangToSourcesMojo extends AbstractMojo { public YangToSourcesMojo() { super(); - parser = new YangModelParserImpl(); + parser = new YangParserImpl(); } @Override public void execute() throws MojoExecutionException, MojoFailureException { - SchemaContext context = processYang(); + ContextHolder context = processYang(); generateSources(context); + generateResources(); + + closeResources(); } /** * Generate {@link SchemaContext} with {@link YangModelParserImpl} */ - private SchemaContext processYang() throws MojoExecutionException { + private ContextHolder processYang() throws MojoExecutionException { try { - String[] yangFiles = Util.listFilesAsArrayOfPaths(yangFilesRootDir); + List yangFiles = Util + .listFilesAsStream(yangFilesRootDir); + Set yangModules = parser + .parseYangModelsFromStreams(yangFiles); - if (yangFiles.length == 0) + Set yangModulesNames = new HashSet(); + for (Module module : yangModules) { + yangModulesNames.add(module.getName()); + } + + List yangFilesFromDependencies = getFilesFromDependenciesAsStream(); + Set yangModulesFromDependencies = parser + .parseYangModelsFromStreams(yangFilesFromDependencies); + + Set parsedYang = new HashSet(yangModules); + parsedYang.addAll(yangModulesFromDependencies); + + if (yangFiles.isEmpty() && yangFilesFromDependencies.isEmpty()) { getLog().warn( - Util.message("No %s file found in %s", LOG_PREFIX, - Util.YANG_SUFFIX, yangFilesRootDir)); - // TODO only warning or throw exception ? + Util.message( + "No %s file found in %s or in dependencies", + LOG_PREFIX, Util.YANG_SUFFIX, yangFilesRootDir)); + Set names = Collections.emptySet(); + return new ContextHolder(null, names); + } - Set parsedYang = parser.parseYangModels(yangFiles); SchemaContext resolveSchemaContext = parser .resolveSchemaContext(parsedYang); getLog().info( Util.message("%s files parsed from %s", LOG_PREFIX, - Util.YANG_SUFFIX, Arrays.toString(yangFiles))); - return resolveSchemaContext; + Util.YANG_SUFFIX, yangFiles)); + return new ContextHolder(resolveSchemaContext, yangModulesNames); // MojoExecutionException is thrown since execution cannot continue } catch (Exception e) { @@ -115,10 +170,172 @@ public final class YangToSourcesMojo extends AbstractMojo { } } + private void generateResources() throws MojoExecutionException, + MojoFailureException { + if (resourceProviders.length == 0) { + getLog().warn( + Util.message("No resource provider classes provided", + LOG_PREFIX)); + return; + } + + Resource res = new Resource(); + String baseDirName = project.getBasedir().getAbsolutePath(); + res.setDirectory(baseDirName + OUTPUT_RESOURCE_DIR); + res.setTargetPath(INPUT_RESOURCE_DIR); + project.addResource(res); + + Map thrown = Maps.newHashMap(); + + Collection yangFiles = new ArrayList(); + + // load files from yang root + yangFiles.addAll(getFilesFromYangRoot()); + + // load files from dependencies + Collection filesFromDependencies = getFilesFromDependencies(); + yangFiles.addAll(filesFromDependencies); + + for (ResourceProviderArg resourceProvider : resourceProviders) { + try { + provideResourcesWithOneProvider(yangFiles, resourceProvider); + } catch (Exception e) { + // try other generators, exception will be thrown after + getLog().error( + Util.message( + "Unable to provide resources with %s resource provider", + LOG_PREFIX, + resourceProvider.getResourceProviderClass()), e); + thrown.put(resourceProvider.getResourceProviderClass(), e + .getClass().getCanonicalName()); + } + } + + if (!thrown.isEmpty()) { + String message = Util + .message( + "One or more code resource provider failed, including failed list(resourceProviderClass=exception) %s", + LOG_PREFIX, thrown.toString()); + getLog().error(message); + throw new MojoFailureException(message); + } + } + + private Collection getFilesFromYangRoot() { + Collection yangFilesLoaded = null; + + File rootDir = new File(yangFilesRootDir); + try { + if (rootDir.isAbsolute()) { + yangFilesLoaded = Util.listFiles(yangFilesRootDir); + } else { + String path = project.getBasedir().getAbsolutePath() + + File.separator + yangFilesRootDir; + yangFilesLoaded = Util.listFiles(path); + } + } catch (FileNotFoundException e) { + getLog().warn( + "yangFilesRootDir[" + rootDir.getAbsolutePath() + + "] does not exists."); + yangFilesLoaded = new ArrayList(); + } + + Collection yangFiles = new ArrayList(yangFilesLoaded); + + try { + for (File yangFile : yangFilesLoaded) { + InputStream is = new FileInputStream(yangFile); + yangFiles.add(createFileFromStream(is, + project.getBasedir().getAbsolutePath() + + OUTPUT_RESOURCE_DIR + yangFile.getName())); + resources.add(is); + } + } catch (IOException e) { + getLog().warn("Exception while loading yang files.", e); + } + return yangFiles; + } + + private Collection getFilesFromDependencies() { + Collection yangFiles = new ArrayList(); + + try { + List filesOnCp = Util.getClassPath(project); + List filter = Lists.newArrayList(".yang"); + for (File file : filesOnCp) { + ZipFile zip = new ZipFile(file); + Enumeration entries = zip.entries(); + + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String entryName = entry.getName(); + if (entryName.startsWith(INPUT_RESOURCE_DIR)) { + if (entry.isDirectory()) { + continue; + } + if (!Util.acceptedFilter(entryName, filter)) { + continue; + } + InputStream entryStream = zip.getInputStream(entry); + String newEntryName = entryName + .substring(INPUT_RESOURCE_DIR.length()); + File tmp = Files.createTempDir(); + File f = createFileFromStream(entryStream, + tmp.getAbsolutePath() + newEntryName); + yangFiles.add(f); + + resources.add(entryStream); + } + } + + resources.add(zip); + } + } catch (Exception e) { + getLog().warn("Exception while loading external yang files.", e); + } + return yangFiles; + } + + private File createFileFromStream(InputStream is, String absoluteName) + throws IOException { + File f = new File(absoluteName); + if (!f.exists()) { + f.getParentFile().mkdirs(); + } + f.createNewFile(); + + FileOutputStream fos = new FileOutputStream(f); + IOUtils.copy(is, fos); + return f; + } + + /** + * Instantiate provider from class and call required method + */ + private void provideResourcesWithOneProvider(Collection yangFiles, + ResourceProviderArg resourceProvider) + throws ClassNotFoundException, InstantiationException, + IllegalAccessException { + + resourceProvider.check(); + + ResourceGenerator g = Util.getInstance( + resourceProvider.getResourceProviderClass(), + ResourceGenerator.class); + getLog().info( + Util.message("Resource provider instantiated from %s", + LOG_PREFIX, resourceProvider.getResourceProviderClass())); + + g.generateResourceFiles(yangFiles, resourceProvider.getOutputBaseDir()); + getLog().info( + Util.message("Resource provider %s call successful", + LOG_PREFIX, resourceProvider.getResourceProviderClass())); + } + /** * Call generate on every generator from plugin configuration */ - private void generateSources(SchemaContext context) + private void generateSources(ContextHolder context) throws MojoFailureException { if (codeGenerators.length == 0) { getLog().warn( @@ -127,12 +344,9 @@ public final class YangToSourcesMojo extends AbstractMojo { } Map thrown = Maps.newHashMap(); - for (CodeGeneratorArg codeGenerator : codeGenerators) { try { - generateSourcesWithOneGenerator(context, codeGenerator); - } catch (Exception e) { // try other generators, exception will be thrown after getLog().error( @@ -158,23 +372,104 @@ public final class YangToSourcesMojo extends AbstractMojo { /** * Instantiate generator from class and call required method */ - private void generateSourcesWithOneGenerator(SchemaContext context, - CodeGeneratorArg codeGenerator) throws ClassNotFoundException, - InstantiationException, IllegalAccessException { + private void generateSourcesWithOneGenerator(ContextHolder context, + CodeGeneratorArg codeGeneratorCfg) throws ClassNotFoundException, + InstantiationException, IllegalAccessException, IOException { - codeGenerator.check(); + codeGeneratorCfg.check(); CodeGenerator g = Util.getInstance( - codeGenerator.getCodeGeneratorClass(), CodeGenerator.class); + codeGeneratorCfg.getCodeGeneratorClass(), CodeGenerator.class); getLog().info( Util.message("Code generator instantiated from %s", LOG_PREFIX, - codeGenerator.getCodeGeneratorClass())); + codeGeneratorCfg.getCodeGeneratorClass())); - Collection generated = g.generateSources(context, - codeGenerator.getOutputBaseDir()); + File outputDir = codeGeneratorCfg.getOutputBaseDir(); + if (project != null && outputDir != null) { + project.addCompileSourceRoot(outputDir.getPath()); + } + Collection generated = g.generateSources(context.getContext(), + outputDir, context.getYangModulesNames()); getLog().info( Util.message("Sources generated by %s: %s", LOG_PREFIX, - codeGenerator.getCodeGeneratorClass(), generated)); + codeGeneratorCfg.getCodeGeneratorClass(), generated)); + } + + /** + * Collection of resources which should be closed after use. + */ + private final List resources = new ArrayList(); + + /** + * Search for yang files in dependent projects. + * + * @return files found as List of InputStream + */ + private List getFilesFromDependenciesAsStream() { + final List yangsFromDependencies = new ArrayList(); + try { + List filesOnCp = Util.getClassPath(project); + + List filter = Lists.newArrayList(".yang"); + for (File file : filesOnCp) { + ZipFile zip = new ZipFile(file); + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String entryName = entry.getName(); + + if (entryName.startsWith(INPUT_RESOURCE_DIR)) { + if (entry.isDirectory()) { + continue; + } + if (!Util.acceptedFilter(entryName, filter)) { + continue; + } + + InputStream entryStream = zip.getInputStream(entry); + yangsFromDependencies.add(entryStream); + resources.add(entryStream); + } + + } + resources.add(zip); + } + } catch (Exception e) { + getLog().warn("Exception while searching yangs in dependencies", e); + } + return yangsFromDependencies; + } + + /** + * Internal utility method for closing open resources. + */ + private void closeResources() { + for (Closeable resource : resources) { + try { + resource.close(); + } catch (IOException e) { + getLog().warn("Failed to close resources: " + resource, e); + } + } + } + + private class ContextHolder { + private final SchemaContext context; + private final Set yangModulesNames; + + private ContextHolder(SchemaContext context, + Set yangModulesNames) { + this.context = context; + this.yangModulesNames = yangModulesNames; + } + + public SchemaContext getContext() { + return context; + } + + public Set getYangModulesNames() { + return yangModulesNames; + } } }