blob: 9e2626611b357dcf33aee0ad27dcd16c534b7a44 [file] [log] [blame]
Thomas Vachuska8c8b0372015-03-10 11:11:24 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.maven;
17
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070018import com.google.common.io.ByteStreams;
19import org.apache.commons.configuration.ConfigurationException;
20import org.apache.commons.configuration.XMLConfiguration;
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070021import org.apache.maven.plugin.AbstractMojo;
22import org.apache.maven.plugin.MojoExecutionException;
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070023import org.apache.maven.plugins.annotations.Component;
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070024import org.apache.maven.plugins.annotations.LifecyclePhase;
25import org.apache.maven.plugins.annotations.Mojo;
26import org.apache.maven.plugins.annotations.Parameter;
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070027import org.apache.maven.project.MavenProject;
28import org.apache.maven.project.MavenProjectHelper;
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070029
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070030import java.io.File;
31import java.io.FileInputStream;
32import java.io.FileNotFoundException;
33import java.io.FileOutputStream;
34import java.io.IOException;
35import java.io.InputStream;
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070036import java.util.List;
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070037import java.util.stream.Collectors;
38import java.util.zip.ZipEntry;
39import java.util.zip.ZipOutputStream;
40
41import static org.codehaus.plexus.util.FileUtils.*;
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070042
43/**
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070044 * Produces ONOS application archive using the app.xml file information.
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070045 */
46@Mojo(name = "app", defaultPhase = LifecyclePhase.PACKAGE)
47public class OnosAppMojo extends AbstractMojo {
48
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070049 private static final String APP = "app";
50 private static final String NAME = "[@name]";
51 private static final String VERSION = "[@version]";
52 private static final String FEATURES_REPO = "[@featuresRepo]";
53 private static final String DESCRIPTION = "description";
54 private static final String ARTIFACT = "artifact";
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070055
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070056 private static final String APP_XML = "app.xml";
57 private static final String FEATURES_XML = "features.xml";
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070058
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070059 private static final String MVN_URL = "mvn:";
60 private static final String M2_REPOSITORY = ".m2/repository";
61 private static final String M2_PREFIX = "m2";
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070062
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070063 private static final String SNAPSHOT = "-SNAPSHOT";
64 private static final String JAR = "jar";
65 private static final String XML = "xml";
66 private static final String APP_ZIP = "oar";
67 private static final String PACKAGE_DIR = "oar";
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070068
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070069 private static final int BUFFER_ZIZE = 8192;
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070070
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070071 private String name, version;
72 private String description, featuresRepo;
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070073 private List<String> artifacts;
74
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070075 /**
76 * The project base directory.
77 */
78 @Parameter(defaultValue = "${basedir}")
79 protected File baseDir;
Thomas Vachuska8c8b0372015-03-10 11:11:24 -070080
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -070081 /**
82 * The directory where the generated catalogue file will be put.
83 */
84 @Parameter(defaultValue = "${project.build.directory}")
85 protected File dstDirectory;
86
87 /**
88 * The project group ID.
89 */
90 @Parameter(defaultValue = "${project.groupId}")
91 protected String projectGroupId;
92
93 /**
94 * The project artifact ID.
95 */
96 @Parameter(defaultValue = "${project.artifactId}")
97 protected String projectArtifactId;
98
99 /**
100 * The project version.
101 */
102 @Parameter(defaultValue = "${project.version}")
103 protected String projectVersion;
104
105 /**
106 * The project version.
107 */
108 @Parameter(defaultValue = "${project.description}")
109 protected String projectDescription;
110
111 /**
112 * Maven project
113 */
114 @Component
115 private MavenProject project;
116
117 /**
118 * Maven project helper.
119 */
120 @Component
121 private MavenProjectHelper projectHelper;
122
123
124 private File m2Directory;
125 protected File stageDirectory;
126 protected String projectPath;
127 protected String featureVersion;
128
129 @Override
Thomas Vachuska8c8b0372015-03-10 11:11:24 -0700130 public void execute() throws MojoExecutionException {
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -0700131 File appFile = new File(baseDir, APP_XML);
132 File featuresFile = new File(baseDir, FEATURES_XML);
Thomas Vachuska8c8b0372015-03-10 11:11:24 -0700133
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -0700134 if (!appFile.exists()) {
135 return;
136 }
137
138 m2Directory = new File(System.getProperty("user.home"), M2_REPOSITORY);
139 stageDirectory = new File(dstDirectory, PACKAGE_DIR);
140 featureVersion = projectVersion.replace(SNAPSHOT, "");
141 projectPath = M2_PREFIX + "/" + artifactDir(projectGroupId, projectArtifactId, projectVersion);
142
143 loadAppFile(appFile);
144
145 // If there are any artifacts, stage the
146 if (!artifacts.isEmpty()) {
147 getLog().info("Building ONOS application package for " + name + " (v" + version + ")");
148 artifacts.forEach(a -> getLog().debug("Including artifact: " + a));
149
150 stageDirectory.mkdirs();
151 processAppXml(appFile);
152 processFeaturesXml(featuresFile);
153 processArtifacts();
154 generateAppPackage();
155 }
156 }
157
158 // Loads the app.xml file.
159 private void loadAppFile(File appFile) throws MojoExecutionException {
160 XMLConfiguration xml = new XMLConfiguration();
161 xml.setRootElementName(APP);
162
163 try (FileInputStream stream = new FileInputStream(appFile)) {
164 xml.load(stream);
165 xml.setAttributeSplittingDisabled(true);
166 xml.setDelimiterParsingDisabled(true);
167
168 name = xml.getString(NAME);
169 version = eval(xml.getString(VERSION));
170 description = xml.getString(DESCRIPTION)
171 .replaceAll("\\$\\{project.description\\}", projectDescription);
172 featuresRepo = eval(xml.getString(FEATURES_REPO));
173
174 artifacts = xml.configurationsAt(ARTIFACT).stream()
175 .map(cfg -> eval(cfg.getRootNode().getValue().toString()))
176 .collect(Collectors.toList());
177
178 } catch (ConfigurationException e) {
179 throw new MojoExecutionException("Unable to parse app.xml file", e);
180 } catch (FileNotFoundException e) {
181 throw new MojoExecutionException("Unable to find app.xml file", e);
182 } catch (IOException e) {
183 throw new MojoExecutionException("Unable to read app.xml file", e);
184 }
185 }
186
187 // Processes and stages the app.xml file.
188 private void processAppXml(File appFile) throws MojoExecutionException {
189 try {
190 File file = new File(stageDirectory, APP_XML);
191 forceMkdir(stageDirectory);
192 String s = eval(fileRead(appFile));
193 fileWrite(file.getAbsolutePath(), s);
194 } catch (IOException e) {
195 throw new MojoExecutionException("Unable to process app.xml", e);
196 }
197 }
198
199 private void processFeaturesXml(File featuresFile) throws MojoExecutionException {
200 boolean specified = featuresRepo != null && featuresRepo.length() > 0;
201
202 // If featuresRepo attribute is specified and there is a features.xml
203 // file present, add the features repo as an artifact
204 try {
205 if (specified && featuresFile.exists()) {
206 processFeaturesXml(new FileInputStream(featuresFile));
207 } else if (specified) {
208 processFeaturesXml(getClass().getResourceAsStream(FEATURES_XML));
209 }
210 } catch (FileNotFoundException e) {
211 throw new MojoExecutionException("Unable to find features.xml file", e);
212 } catch (IOException e) {
213 throw new MojoExecutionException("Unable to process features.xml file", e);
214 }
215 }
216
217 // Processes and stages the features.xml file.
218 private void processFeaturesXml(InputStream stream) throws IOException {
219 String featuresArtifact =
220 artifactFile(projectArtifactId, projectVersion, XML, "features");
221 File dstDir = new File(stageDirectory, projectPath);
222 forceMkdir(dstDir);
223 String s = eval(new String(ByteStreams.toByteArray(stream)));
224 fileWrite(new File(dstDir, featuresArtifact).getAbsolutePath(), s);
225 }
226
227 // Stages all artifacts.
Thomas Vachuskaa7a02202015-04-14 23:13:02 -0700228 private void processArtifacts() throws MojoExecutionException {
229 for (String artifact : artifacts) {
230 processArtifact(artifact);
231 }
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -0700232 }
233
234 // Stages the specified artifact.
Thomas Vachuskaa7a02202015-04-14 23:13:02 -0700235 private void processArtifact(String artifact) throws MojoExecutionException {
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -0700236 if (!artifact.startsWith(MVN_URL)) {
Thomas Vachuskaa7a02202015-04-14 23:13:02 -0700237 throw new MojoExecutionException("Unsupported artifact URL:" + artifact);
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -0700238 }
239
240 String[] fields = artifact.substring(4).split("/");
241 if (fields.length < 3) {
Thomas Vachuskaa7a02202015-04-14 23:13:02 -0700242 throw new MojoExecutionException("Illegal artifact URL:" + artifact);
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -0700243 }
244
245 try {
246 String file = artifactFile(fields);
247
248 if (projectGroupId.equals(fields[0]) && projectArtifactId.equals(fields[1])) {
249 // Local artifact is not installed yet, package it from target directory.
250 File dstDir = new File(stageDirectory, projectPath);
251 forceMkdir(dstDir);
252 copyFile(new File(dstDirectory, file), new File(dstDir, file));
253 } else {
254 // Other artifacts are packaged from ~/.m2/repository directory.
255 String m2Path = artifactDir(fields);
256 File srcDir = new File(m2Directory, m2Path);
Thomas Vachuskaa7a02202015-04-14 23:13:02 -0700257 File dstDir = new File(stageDirectory, M2_PREFIX + "/" + m2Path);
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -0700258 forceMkdir(dstDir);
259 copyFile(new File(srcDir, file), new File(dstDir, file));
260 }
261 } catch (IOException e) {
Thomas Vachuskaa7a02202015-04-14 23:13:02 -0700262 throw new MojoExecutionException("Unable to stage artifact " + artifact, e);
Thomas Vachuskaa98ae7f2015-04-14 14:00:36 -0700263 }
264 }
265
266 // Generates the ONOS package ZIP file.
267 private void generateAppPackage() throws MojoExecutionException {
268 File appZip = new File(dstDirectory, artifactFile(projectArtifactId, projectVersion,
269 APP_ZIP, null));
270 try (FileOutputStream fos = new FileOutputStream(appZip);
271 ZipOutputStream zos = new ZipOutputStream(fos)) {
272 zipDirectory("", stageDirectory, zos);
273 projectHelper.attachArtifact(this.project, APP_ZIP, null, appZip);
274 } catch (IOException e) {
275 throw new MojoExecutionException("Unable to compress application package", e);
276 }
277 }
278
279 // Generates artifact directory name from the specified fields.
280 private String artifactDir(String[] fields) {
281 return artifactDir(fields[0], fields[1], fields[2]);
282 }
283
284 // Generates artifact directory name from the specified elements.
285 private String artifactDir(String gid, String aid, String version) {
286 return gid.replace('.', '/') + "/" + aid.replace('.', '/') + "/" + version;
287 }
288
289 // Generates artifact file name from the specified fields.
290 private String artifactFile(String[] fields) {
291 return fields.length < 5 ?
292 artifactFile(fields[1], fields[2],
293 (fields.length < 4 ? JAR : fields[3]), null) :
294 artifactFile(fields[1], fields[2], fields[3], fields[4]);
295 }
296
297 // Generates artifact file name from the specified elements.
298 private String artifactFile(String aid, String version, String type,
299 String classifier) {
300 return classifier == null ? aid + "-" + version + "." + type :
301 aid + "-" + version + "-" + classifier + "." + type;
302 }
303
304 // Returns the given string with project variable substitutions.
305 private String eval(String string) {
306 return string == null ? null :
307 string.replaceAll("\\$\\{project.groupId\\}", projectGroupId)
308 .replaceAll("\\$\\{project.artifactId\\}", projectArtifactId)
309 .replaceAll("\\$\\{project.version\\}", projectVersion)
310 .replaceAll("\\$\\{project.description\\}", description)
311 .replaceAll("\\$\\{feature.version\\}", featureVersion);
312 }
313
314 // Recursively archives the specified directory into a given ZIP stream.
315 private void zipDirectory(String root, File dir, ZipOutputStream zos)
316 throws IOException {
317 byte[] buffer = new byte[BUFFER_ZIZE];
318 File[] files = dir.listFiles();
319 if (files != null && files.length > 0) {
320 for (File file : files) {
321 if (file.isDirectory()) {
322 String path = root + file.getName() + "/";
323 zos.putNextEntry(new ZipEntry(path));
324 zipDirectory(path, file, zos);
325 zos.closeEntry();
326 } else {
327 FileInputStream fin = new FileInputStream(file);
328 zos.putNextEntry(new ZipEntry(root + file.getName()));
329 int length;
330 while ((length = fin.read(buffer)) > 0) {
331 zos.write(buffer, 0, length);
332 }
333 zos.closeEntry();
334 fin.close();
335 }
336 }
337 }
Thomas Vachuska8c8b0372015-03-10 11:11:24 -0700338 }
339}