*/
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;
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
*/
@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
@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;
public YangToSourcesMojo() {
super();
- parser = new YangModelParserImpl();
+ parser = new YangParserImpl();
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
SchemaContext context = processYang();
generateSources(context);
+ generateResources();
+
+ closeResources();
}
/**
*/
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(
- Util.message("No %s file found in %s", LOG_PREFIX,
- Util.YANG_SUFFIX, yangFilesRootDir));
+ Util.message(
+ "No %s file found in %s or in dependencies",
+ LOG_PREFIX, Util.YANG_SUFFIX, yangFilesRootDir));
return null;
}
Set<Module> parsedYang = parser
- .parseYangModels(new ArrayList<File>(yangFiles));
+ .parseYangModelsFromStreams(new ArrayList<InputStream>(
+ yangFiles));
SchemaContext resolveSchemaContext = parser
.resolveSchemaContext(parsedYang);
getLog().info(
}
}
+ 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
+ Collection<File> 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<File> getFilesFromYangRoot() {
+ Collection<File> 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<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 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<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
*/
}
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(
*/
private void generateSourcesWithOneGenerator(SchemaContext context,
CodeGeneratorArg codeGeneratorCfg) throws ClassNotFoundException,
- InstantiationException, IllegalAccessException {
+ InstantiationException, IllegalAccessException, IOException {
codeGeneratorCfg.check();
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);
+ }
+ }
+ }
+
}