Fixed relative/absolute yang files directory resolving.
[controller.git] / opendaylight / sal / yang-prototype / code-generator / maven-yang-plugin / src / main / java / org / opendaylight / controller / yang2sources / plugin / YangToSourcesMojo.java
index 05d9c0cd2c54488e5815b4701ec862c7744f89f3..cfaa8a0874aa24fcfd798679605eb7349d9983ef 100644 (file)
@@ -7,13 +7,24 @@
  */
 package org.opendaylight.controller.yang2sources.plugin;
 
+import java.io.Closeable;
 import java.io.File;
+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.Enumeration;
+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;
@@ -27,9 +38,12 @@ 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.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;
 
 /**
@@ -47,8 +61,9 @@ import com.google.common.collect.Maps;
  */
 @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/yangs/";
+    private static final String OUTPUT_RESOURCE_DIR = "/target/external-resources/";
 
     /**
      * Classes implementing {@link CodeGenerator} interface. An instance will be
@@ -66,15 +81,26 @@ public final class YangToSourcesMojo extends AbstractMojo {
     @Parameter(required = true)
     private String yangFilesRootDir;
 
+    /**
+     * 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;
@@ -89,6 +115,9 @@ public final class YangToSourcesMojo extends AbstractMojo {
     public void execute() throws MojoExecutionException, MojoFailureException {
         SchemaContext context = processYang();
         generateSources(context);
+        generateResources();
+
+        closeResources();
     }
 
     /**
@@ -96,7 +125,9 @@ public final class YangToSourcesMojo extends AbstractMojo {
      */
     private SchemaContext processYang() throws MojoExecutionException {
         try {
-            Collection<File> yangFiles = Util.listFiles(yangFilesRootDir);
+            Collection<InputStream> yangFiles = Util
+                    .listFilesAsStream(yangFilesRootDir);
+            yangFiles.addAll(getFilesFromDependenciesAsStream());
 
             if (yangFiles.isEmpty()) {
                 getLog().warn(
@@ -106,7 +137,8 @@ public final class YangToSourcesMojo extends AbstractMojo {
             }
 
             Set<Module> parsedYang = parser
-                    .parseYangModels(new ArrayList<File>(yangFiles));
+                    .parseYangModelsFromStreams(new ArrayList<InputStream>(
+                            yangFiles));
             SchemaContext resolveSchemaContext = parser
                     .resolveSchemaContext(parsedYang);
             getLog().info(
@@ -123,6 +155,158 @@ 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<String, String> thrown = Maps.newHashMap();
+
+        Collection<File> yangFiles = new ArrayList<File>();
+
+        // load files from yang root
+        yangFiles.addAll(getFilesFromYangRoot());
+
+        // load files from dependencies
+        yangFiles.addAll(getFilesFromDependencies());
+
+
+        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<File> getFilesFromYangRoot() {
+        Collection<File> yangFilesLoaded = null;
+
+        File rootDir = new File(yangFilesRootDir);
+        try {
+            if(!rootDir.isAbsolute()) {
+                yangFilesLoaded = Util.listFiles(project.getBasedir().getAbsolutePath() + yangFilesRootDir);
+            } else {
+                yangFilesLoaded = Util.listFiles(yangFilesRootDir);
+            }
+
+        } catch(FileNotFoundException e) {
+            getLog().warn("Directory '" + yangFilesRootDir + "' does not exists.");
+            yangFilesLoaded = new ArrayList<File>();
+        }
+        Collection<File> yangFiles = new ArrayList<File>(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<File> getFilesFromDependencies() {
+        Collection<File> yangFiles = new ArrayList<File>();
+
+        try {
+            List<File> filesOnCp = Util.getClassPath(project);
+            List<String> filter = Lists.newArrayList(".yang");
+            for (File file : filesOnCp) {
+                ZipFile zip = new ZipFile(file);
+                Enumeration<? extends ZipEntry> 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 f = createFileFromStream(entryStream, project.getBasedir().getAbsolutePath() + OUTPUT_RESOURCE_DIR + 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<File> 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
      */
@@ -135,12 +319,9 @@ public final class YangToSourcesMojo extends AbstractMojo {
         }
 
         Map<String, String> thrown = Maps.newHashMap();
-
         for (CodeGeneratorArg codeGenerator : codeGenerators) {
             try {
-
                 generateSourcesWithOneGenerator(context, codeGenerator);
-
             } catch (Exception e) {
                 // try other generators, exception will be thrown after
                 getLog().error(
@@ -188,4 +369,62 @@ public final class YangToSourcesMojo extends AbstractMojo {
                         codeGeneratorCfg.getCodeGeneratorClass(), generated));
     }
 
+    /**
+     * Collection of resources which should be closed after use.
+     */
+    private final List<Closeable> resources = new ArrayList<Closeable>();
+
+    /**
+     * Search for yang files in dependent projects.
+     *
+     * @return files found as List of InputStream
+     */
+    private List<InputStream> getFilesFromDependenciesAsStream() {
+        final List<InputStream> yangsFromDependencies = new ArrayList<InputStream>();
+        try {
+            List<File> filesOnCp = Util.getClassPath(project);
+
+            List<String> filter = Lists.newArrayList(".yang");
+            for (File file : filesOnCp) {
+                ZipFile zip = new ZipFile(file);
+                Enumeration<? extends ZipEntry> 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);
+            }
+        }
+    }
+
 }