diff options
Diffstat (limited to 'framework/src/onos/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosAppMojo.java')
-rw-r--r-- | framework/src/onos/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosAppMojo.java | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/framework/src/onos/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosAppMojo.java b/framework/src/onos/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosAppMojo.java new file mode 100644 index 00000000..bfc6127a --- /dev/null +++ b/framework/src/onos/tools/package/maven-plugin/src/main/java/org/onosproject/maven/OnosAppMojo.java @@ -0,0 +1,372 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.maven; + +import com.google.common.collect.ImmutableList; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Component; +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.project.MavenProject; +import org.apache.maven.project.MavenProjectHelper; + +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.List; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static com.google.common.io.ByteStreams.toByteArray; +import static org.codehaus.plexus.util.FileUtils.*; + +/** + * Produces ONOS application archive using the app.xml file information. + */ +@Mojo(name = "app", defaultPhase = LifecyclePhase.PACKAGE) +public class OnosAppMojo extends AbstractMojo { + + private static final String APP = "app"; + private static final String NAME = "[@name]"; + private static final String VERSION = "[@version]"; + private static final String FEATURES_REPO = "[@featuresRepo]"; + private static final String ARTIFACT = "artifact"; + + private static final String APP_XML = "app.xml"; + private static final String FEATURES_XML = "features.xml"; + + private static final String MVN_URL = "mvn:"; + private static final String M2_PREFIX = "m2"; + + private static final String ONOS_APP_NAME = "onos.app.name"; + private static final String ONOS_APP_ORIGIN = "onos.app.origin"; + + private static final String JAR = "jar"; + private static final String XML = "xml"; + private static final String APP_ZIP = "oar"; + private static final String PACKAGE_DIR = "oar"; + + private static final String DEFAULT_ORIGIN = "ON.Lab"; + private static final String DEFAULT_VERSION = "${project.version}"; + + private static final String DEFAULT_FEATURES_REPO = + "mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"; + private static final String DEFAULT_ARTIFACT = + "mvn:${project.groupId}/${project.artifactId}/${project.version}"; + + private static final int BUFFER_SIZE = 8192; + + private String name; + private String origin; + private String version = DEFAULT_VERSION; + private String featuresRepo = DEFAULT_FEATURES_REPO; + private List<String> artifacts; + + /** + * The project base directory. + */ + @Parameter(defaultValue = "${basedir}") + protected File baseDir; + + /** + * The directory where the generated catalogue file will be put. + */ + @Parameter(defaultValue = "${project.build.directory}") + protected File dstDirectory; + + /** + * The project group ID. + */ + @Parameter(defaultValue = "${project.groupId}") + protected String projectGroupId; + + /** + * The project artifact ID. + */ + @Parameter(defaultValue = "${project.artifactId}") + protected String projectArtifactId; + + /** + * The project version. + */ + @Parameter(defaultValue = "${project.version}") + protected String projectVersion; + + /** + * The project version. + */ + @Parameter(defaultValue = "${project.description}") + protected String projectDescription; + + @Parameter(defaultValue = "${localRepository}") + protected ArtifactRepository localRepository; + + /** + * Maven project + */ + @Parameter(defaultValue = "${project}") + protected MavenProject project; + + /** + * Maven project helper. + */ + @Component + protected MavenProjectHelper projectHelper; + + + private File m2Directory; + protected File stageDirectory; + protected String projectPath; + + @Override + public void execute() throws MojoExecutionException { + File appFile = new File(baseDir, APP_XML); + File featuresFile = new File(baseDir, FEATURES_XML); + + name = (String) project.getProperties().get(ONOS_APP_NAME); + + // If neither the app.xml file exists, nor the onos.app.name property + // is defined, there is nothing for this Mojo to do, so bail. + if (!appFile.exists() && name == null) { + return; + } + + m2Directory = new File(localRepository.getBasedir()); + stageDirectory = new File(dstDirectory, PACKAGE_DIR); + projectPath = M2_PREFIX + "/" + artifactDir(projectGroupId, projectArtifactId, projectVersion); + + origin = (String) project.getProperties().get(ONOS_APP_ORIGIN); + origin = origin != null ? origin : DEFAULT_ORIGIN; + + if (appFile.exists()) { + loadAppFile(appFile); + } else { + artifacts = ImmutableList.of(eval(DEFAULT_ARTIFACT)); + } + + // If there are any artifacts, stage the + if (!artifacts.isEmpty()) { + getLog().info("Building ONOS application package for " + name + " (v" + eval(version) + ")"); + artifacts.forEach(a -> getLog().debug("Including artifact: " + a)); + + if (stageDirectory.exists() || stageDirectory.mkdirs()) { + processAppXml(appFile); + processFeaturesXml(featuresFile); + processArtifacts(); + generateAppPackage(); + } else { + throw new MojoExecutionException("Unable to create directory: " + stageDirectory); + } + } + } + + // Loads the app.xml file. + private void loadAppFile(File appFile) throws MojoExecutionException { + XMLConfiguration xml = new XMLConfiguration(); + xml.setRootElementName(APP); + + try (FileInputStream stream = new FileInputStream(appFile)) { + xml.load(stream); + xml.setAttributeSplittingDisabled(true); + xml.setDelimiterParsingDisabled(true); + + name = xml.getString(NAME); + version = eval(xml.getString(VERSION)); + featuresRepo = eval(xml.getString(FEATURES_REPO)); + + artifacts = xml.configurationsAt(ARTIFACT).stream() + .map(cfg -> eval(cfg.getRootNode().getValue().toString())) + .collect(Collectors.toList()); + + } catch (ConfigurationException e) { + throw new MojoExecutionException("Unable to parse app.xml file", e); + } catch (FileNotFoundException e) { + throw new MojoExecutionException("Unable to find app.xml file", e); + } catch (IOException e) { + throw new MojoExecutionException("Unable to read app.xml file", e); + } + } + + // Processes and stages the app.xml file. + private void processAppXml(File appFile) throws MojoExecutionException { + try { + File file = new File(stageDirectory, APP_XML); + forceMkdir(stageDirectory); + String contents; + + if (appFile.exists()) { + contents = fileRead(appFile); + } else { + byte[] bytes = toByteArray(getClass().getResourceAsStream(APP_XML)); + contents = new String(bytes); + } + fileWrite(file.getAbsolutePath(), eval(contents)); + } catch (IOException e) { + throw new MojoExecutionException("Unable to process app.xml", e); + } + } + + private void processFeaturesXml(File featuresFile) throws MojoExecutionException { + boolean specified = featuresRepo != null && featuresRepo.length() > 0; + + // If featuresRepo attribute is specified and there is a features.xml + // file present, add the features repo as an artifact + try { + if (specified && featuresFile.exists()) { + processFeaturesXml(new FileInputStream(featuresFile)); + } else if (specified) { + processFeaturesXml(getClass().getResourceAsStream(FEATURES_XML)); + } + } catch (FileNotFoundException e) { + throw new MojoExecutionException("Unable to find features.xml file", e); + } catch (IOException e) { + throw new MojoExecutionException("Unable to process features.xml file", e); + } + } + + // Processes and stages the features.xml file. + private void processFeaturesXml(InputStream stream) throws IOException { + String featuresArtifact = + artifactFile(projectArtifactId, projectVersion, XML, "features"); + File dstDir = new File(stageDirectory, projectPath); + forceMkdir(dstDir); + String s = eval(new String(toByteArray(stream))); + fileWrite(new File(dstDir, featuresArtifact).getAbsolutePath(), s); + } + + // Stages all artifacts. + private void processArtifacts() throws MojoExecutionException { + for (String artifact : artifacts) { + processArtifact(artifact); + } + } + + // Stages the specified artifact. + private void processArtifact(String artifact) throws MojoExecutionException { + if (!artifact.startsWith(MVN_URL)) { + throw new MojoExecutionException("Unsupported artifact URL:" + artifact); + } + + String[] fields = artifact.substring(4).split("/"); + if (fields.length < 3) { + throw new MojoExecutionException("Illegal artifact URL:" + artifact); + } + + try { + String file = artifactFile(fields); + + if (projectGroupId.equals(fields[0]) && projectArtifactId.equals(fields[1])) { + // Local artifact is not installed yet, package it from target directory. + File dstDir = new File(stageDirectory, projectPath); + forceMkdir(dstDir); + copyFile(new File(dstDirectory, file), new File(dstDir, file)); + } else { + // Other artifacts are packaged from ~/.m2/repository directory. + String m2Path = artifactDir(fields); + File srcDir = new File(m2Directory, m2Path); + File dstDir = new File(stageDirectory, M2_PREFIX + "/" + m2Path); + forceMkdir(dstDir); + copyFile(new File(srcDir, file), new File(dstDir, file)); + } + } catch (IOException e) { + throw new MojoExecutionException("Unable to stage artifact " + artifact, e); + } + } + + // Generates the ONOS package ZIP file. + private void generateAppPackage() throws MojoExecutionException { + File appZip = new File(dstDirectory, artifactFile(projectArtifactId, projectVersion, + APP_ZIP, null)); + try (FileOutputStream fos = new FileOutputStream(appZip); + ZipOutputStream zos = new ZipOutputStream(fos)) { + zipDirectory("", stageDirectory, zos); + projectHelper.attachArtifact(this.project, APP_ZIP, null, appZip); + } catch (IOException e) { + throw new MojoExecutionException("Unable to compress application package", e); + } + } + + // Generates artifact directory name from the specified fields. + private String artifactDir(String[] fields) { + return artifactDir(fields[0], fields[1], fields[2]); + } + + // Generates artifact directory name from the specified elements. + private String artifactDir(String gid, String aid, String version) { + return gid.replace('.', '/') + "/" + aid.replace('.', '/') + "/" + version; + } + + // Generates artifact file name from the specified fields. + private String artifactFile(String[] fields) { + return fields.length < 5 ? + artifactFile(fields[1], fields[2], + (fields.length < 4 ? JAR : fields[3]), null) : + artifactFile(fields[1], fields[2], fields[3], fields[4]); + } + + // Generates artifact file name from the specified elements. + private String artifactFile(String aid, String version, String type, + String classifier) { + return classifier == null ? aid + "-" + version + "." + type : + aid + "-" + version + "-" + classifier + "." + type; + } + + // Returns the given string with project variable substitutions. + private String eval(String string) { + return string == null ? null : + string.replaceAll("\\$\\{onos.app.name\\}", name) + .replaceAll("\\$\\{onos.app.origin\\}", origin) + .replaceAll("\\$\\{project.groupId\\}", projectGroupId) + .replaceAll("\\$\\{project.artifactId\\}", projectArtifactId) + .replaceAll("\\$\\{project.version\\}", projectVersion) + .replaceAll("\\$\\{project.description\\}", projectDescription); + } + + // Recursively archives the specified directory into a given ZIP stream. + private void zipDirectory(String root, File dir, ZipOutputStream zos) + throws IOException { + byte[] buffer = new byte[BUFFER_SIZE]; + File[] files = dir.listFiles(); + if (files != null && files.length > 0) { + for (File file : files) { + if (file.isDirectory()) { + String path = root + file.getName() + "/"; + zos.putNextEntry(new ZipEntry(path)); + zipDirectory(path, file, zos); + zos.closeEntry(); + } else { + FileInputStream fin = new FileInputStream(file); + zos.putNextEntry(new ZipEntry(root + file.getName())); + int length; + while ((length = fin.read(buffer)) > 0) { + zos.write(buffer, 0, length); + } + zos.closeEntry(); + fin.close(); + } + } + } + } +} |