2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
19 package org.apache.karaf.tooling;
22 import java.io.FileInputStream;
23 import java.io.InputStream;
24 import java.nio.file.Files;
25 import java.nio.file.attribute.PosixFilePermissions;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
31 import org.apache.karaf.profile.assembly.Builder;
32 import org.apache.karaf.tooling.utils.IoUtils;
33 import org.apache.karaf.tooling.utils.MavenUtil;
34 import org.apache.karaf.tooling.utils.MojoSupport;
35 import org.apache.karaf.tools.utils.model.KarafPropertyEdits;
36 import org.apache.karaf.tools.utils.model.io.stax.KarafPropertyInstructionsModelStaxReader;
37 import org.apache.maven.artifact.Artifact;
38 import org.apache.maven.plugin.MojoExecutionException;
39 import org.apache.maven.plugin.MojoFailureException;
40 import org.apache.maven.plugins.annotations.LifecyclePhase;
41 import org.apache.maven.plugins.annotations.Mojo;
42 import org.apache.maven.plugins.annotations.Parameter;
43 import org.apache.maven.plugins.annotations.ResolutionScope;
46 import java.io.FileInputStream;
47 import java.io.InputStream;
48 import java.nio.file.Files;
49 import java.nio.file.attribute.PosixFilePermissions;
50 import java.util.ArrayList;
51 import java.util.List;
54 * Creates a customized Karaf distribution by installing features and setting up
55 * configuration files. The plugin gets features from feature.xml files and KAR
56 * archives declared as dependencies or as files configured with the
57 * featureRespositories parameter. It picks up other files, such as config files,
58 * from ${project.build.directory}/classes. Thus, a file in src/main/resources/etc
59 * will be copied by the resource plugin to ${project.build.directory}/classes/etc,
60 * and then added to the assembly by this goal.
63 @Mojo(name = "assembly", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
64 public class AssemblyMojo extends MojoSupport {
67 * Base directory used to overwrite resources in generated assembly after the build (resource directory).
69 @Parameter(defaultValue = "${project.basedir}/src/main/resources/assembly")
70 protected File sourceDirectory;
73 * Base directory used to copy the resources during the build (working directory).
75 @Parameter(defaultValue = "${project.build.directory}/assembly")
76 protected File workDirectory;
79 * Features configuration file (etc/org.apache.karaf.features.cfg).
81 @Parameter(defaultValue = "${project.build.directory}/assembly/etc/org.apache.karaf.features.cfg")
82 protected File featuresCfgFile;
85 * startup.properties file.
87 @Parameter(defaultValue = "${project.build.directory}/assembly/etc/startup.properties")
88 protected File startupPropertiesFile;
91 * Directory used during build to construction the Karaf system repository.
93 @Parameter(defaultValue="${project.build.directory}/assembly/system")
94 protected File systemDirectory;
97 * default start level for bundles in features that don't specify it.
100 protected int defaultStartLevel = 30;
103 private List<String> startupRepositories;
105 private List<String> bootRepositories;
107 private List<String> installedRepositories;
110 * List of features from runtime-scope features xml and kars to be installed into system and listed in startup.properties.
113 private List<String> startupFeatures;
116 * List of features from runtime-scope features xml and kars to be installed into system repo and listed in features service boot features.
119 private List<String> bootFeatures;
122 * List of features from runtime-scope features xml and kars to be installed into system repo and not mentioned elsewhere.
125 private List<String> installedFeatures;
128 private List<String> blacklistedFeatures;
131 private List<String> startupBundles;
133 private List<String> bootBundles;
135 private List<String> installedBundles;
137 private List<String> blacklistedBundles;
140 private String profilesUri;
143 private List<String> bootProfiles;
146 private List<String> startupProfiles;
149 private List<String> installedProfiles;
152 private List<String> blacklistedProfiles;
155 private Builder.BlacklistPolicy blacklistPolicy = Builder.BlacklistPolicy.Discard;
158 * Ignore the dependency attribute (dependency="[true|false]") on bundle
160 @Parameter(defaultValue = "false")
161 protected boolean ignoreDependencyFlag;
164 * Additional feature repositories
167 protected List<String> featureRepositories;
170 protected List<String> libraries;
173 * Use reference: style urls in startup.properties
175 @Parameter(defaultValue = "false")
176 protected boolean useReferenceUrls;
179 * Include project build output directory in the assembly
181 @Parameter(defaultValue = "true")
182 protected boolean includeBuildOutputDirectory;
185 protected boolean installAllFeaturesByDefault = true;
188 protected Builder.KarafVersion karafVersion = Builder.KarafVersion.v4x;
191 * Specify the version of Java SE to be assumed for osgi.ee.
193 @Parameter(defaultValue = "1.7")
194 protected String javase;
197 * Specify an XML file that instructs this goal to apply edits to
198 * one or more standard Karaf property files.
199 * The contents of this file are documented in detail on
200 * <a href="karaf-property-instructions-model.html">this page</a>.
202 * customize these files without making copies in your resources
203 * directories. Here's a simple example:
206 <property-edits xmlns="http://karaf.apache.org/tools/property-edits/1.0.0">
209 <file>config.properties</file>
210 <operation>put</operation>
211 <key>karaf.framework</key>
212 <value>equinox</value>
215 <file>config.properties</file>
216 <operation>extend</operation>
217 <key>org.osgi.framework.system.capabilities</key>
218 <value>my-magic-capability</value>
221 <file>config.properties</file>
222 <operation prepend='true'>extend</operation>
223 <key>some-other-list</key>
224 <value>my-value-goes-first</value>
231 @Parameter(defaultValue = "${project.basedir}/src/main/karaf/assembly-property-edits.xml")
232 protected String propertyFileEdits;
235 public void execute() throws MojoExecutionException, MojoFailureException {
239 catch (MojoExecutionException | MojoFailureException e) {
242 catch (Exception e) {
243 throw new MojoExecutionException("Unable to build assembly", e);
247 protected void doExecute() throws Exception {
248 startupRepositories = nonNullList(startupRepositories);
249 bootRepositories = nonNullList(bootRepositories);
250 installedRepositories = nonNullList(installedRepositories);
251 startupBundles = nonNullList(startupBundles);
252 bootBundles = nonNullList(bootBundles);
253 installedBundles = nonNullList(installedBundles);
254 blacklistedBundles = nonNullList(blacklistedBundles);
255 startupFeatures = nonNullList(startupFeatures);
256 bootFeatures = nonNullList(bootFeatures);
257 installedFeatures = nonNullList(installedFeatures);
258 blacklistedFeatures = nonNullList(blacklistedFeatures);
259 startupProfiles = nonNullList(startupProfiles);
260 bootProfiles = nonNullList(bootProfiles);
261 installedProfiles = nonNullList(installedProfiles);
262 blacklistedProfiles = nonNullList(blacklistedProfiles);
264 if (!startupProfiles.isEmpty() || !bootProfiles.isEmpty() || !installedProfiles.isEmpty()) {
265 if (profilesUri == null) {
266 throw new IllegalArgumentException("profilesDirectory must be specified");
270 if (featureRepositories != null && !featureRepositories.isEmpty()) {
271 getLog().warn("Use of featureRepositories is deprecated, use startupRepositories, bootRepositories or installedRepositories instead");
272 startupRepositories.addAll(featureRepositories);
273 bootRepositories.addAll(featureRepositories);
274 installedRepositories.addAll(featureRepositories);
277 StringBuilder remote = new StringBuilder();
278 for (Object obj : project.getRemoteProjectRepositories()) {
279 if (remote.length() > 0) {
282 remote.append(invoke(obj, "getUrl"));
283 remote.append("@id=").append(invoke(obj, "getId"));
284 if (!((Boolean) invoke(getPolicy(obj, false), "isEnabled"))) {
285 remote.append("@noreleases");
287 if ((Boolean) invoke(getPolicy(obj, true), "isEnabled")) {
288 remote.append("@snapshots");
291 getLog().info("Using repositories: " + remote.toString());
293 Builder builder = Builder.newInstance();
294 builder.offline(mavenSession.isOffline());
295 builder.localRepository(localRepo.getBasedir());
296 builder.mavenRepositories(remote.toString());
297 builder.javase(javase);
299 // Set up blacklisted items
300 builder.blacklistBundles(blacklistedBundles);
301 builder.blacklistFeatures(blacklistedFeatures);
302 builder.blacklistProfiles(blacklistedProfiles);
303 builder.blacklistPolicy(blacklistPolicy);
305 if (propertyFileEdits != null) {
306 File file = new File(propertyFileEdits);
308 KarafPropertyEdits edits;
309 try (InputStream editsStream = new FileInputStream(propertyFileEdits)) {
310 KarafPropertyInstructionsModelStaxReader kipmsr = new KarafPropertyInstructionsModelStaxReader();
311 edits = kipmsr.read(editsStream, true);
313 builder.propertyEdits(edits);
317 // creating system directory
318 getLog().info("Creating work directory");
319 builder.homeDirectory(workDirectory.toPath());
320 IoUtils.deleteRecursive(workDirectory);
321 workDirectory.mkdirs();
323 List<String> startupKars = new ArrayList<>();
324 List<String> bootKars = new ArrayList<>();
325 List<String> installedKars = new ArrayList<>();
327 // Loading kars and features repositories
328 getLog().info("Loading kar and features repositories dependencies");
329 for (Artifact artifact : project.getDependencyArtifacts()) {
331 switch (artifact.getScope()) {
333 stage = Builder.Stage.Startup;
336 stage = Builder.Stage.Boot;
339 stage = Builder.Stage.Installed;
344 if ("kar".equals(artifact.getType())) {
345 String uri = artifactToMvn(artifact);
347 case Startup: startupKars.add(uri); break;
348 case Boot: bootKars.add(uri); break;
349 case Installed: installedKars.add(uri); break;
351 } else if ("features".equals(artifact.getClassifier())) {
352 String uri = artifactToMvn(artifact);
354 case Startup: startupRepositories.add(uri); break;
355 case Boot: bootRepositories.add(uri); break;
356 case Installed: installedRepositories.add(uri); break;
358 } else if ("jar".equals(artifact.getType()) || "bundle".equals(artifact.getType())) {
359 String uri = artifactToMvn(artifact);
361 case Startup: startupBundles.add(uri); break;
362 case Boot: bootBundles.add(uri); break;
363 case Installed: installedBundles.add(uri); break;
368 builder.karafVersion(karafVersion)
369 .useReferenceUrls(useReferenceUrls)
370 .defaultAddAll(installAllFeaturesByDefault)
371 .ignoreDependencyFlag(ignoreDependencyFlag);
372 if (profilesUri != null) {
373 builder.profilesUris(profilesUri);
375 if (libraries != null) {
376 builder.libraries(libraries.toArray(new String[libraries.size()]));
379 builder.defaultStage(Builder.Stage.Startup)
380 .kars(toArray(startupKars))
381 .repositories(startupFeatures.isEmpty() && startupProfiles.isEmpty() && installAllFeaturesByDefault, toArray(startupRepositories))
382 .features(toArray(startupFeatures))
383 .bundles(toArray(startupBundles))
384 .profiles(toArray(startupProfiles));
386 builder.defaultStage(Builder.Stage.Boot)
387 .kars(toArray(bootKars))
388 .repositories(bootFeatures.isEmpty() && bootProfiles.isEmpty() && installAllFeaturesByDefault, toArray(bootRepositories))
389 .features(toArray(bootFeatures))
390 .bundles(toArray(bootBundles))
391 .profiles(toArray(bootProfiles));
393 builder.defaultStage(Builder.Stage.Installed)
394 .kars(toArray(installedKars))
395 .repositories(installedFeatures.isEmpty() && installedProfiles.isEmpty() && installAllFeaturesByDefault, toArray(installedRepositories))
396 .features(toArray(installedFeatures))
397 .bundles(toArray(installedBundles))
398 .profiles(toArray(installedProfiles));
400 // Generate the assembly
401 builder.generateAssembly();
403 // Include project classes content
404 if (includeBuildOutputDirectory)
405 IoUtils.copyDirectory(new File(project.getBuild().getOutputDirectory()), workDirectory);
407 // Overwrite assembly dir contents
408 if (sourceDirectory.exists())
409 IoUtils.copyDirectory(sourceDirectory, workDirectory);
411 // Chmod the bin/* scripts
412 File[] files = new File(workDirectory, "bin").listFiles();
414 for (File file : files) {
415 if( !file.getName().endsWith(".bat") ) {
417 Files.setPosixFilePermissions(file.toPath(), PosixFilePermissions.fromString("rwxr-xr-x"));
418 } catch (Throwable ignore) {
419 // we tried our best, perhaps the OS does not support posix file perms.
426 private Object invoke(Object object, String getter) throws MojoExecutionException {
428 return object.getClass().getMethod(getter).invoke(object);
429 } catch (Exception e) {
430 throw new MojoExecutionException("Unable to build remote repository from " + object.toString(), e);
434 private Object getPolicy(Object object, boolean snapshots) throws MojoExecutionException {
435 return invoke(object, "getPolicy", new Class[] { Boolean.TYPE }, new Object[] { snapshots });
438 private Object invoke(Object object, String getter, Class[] types, Object[] params) throws MojoExecutionException {
440 return object.getClass().getMethod(getter, types).invoke(object, params);
441 } catch (Exception e) {
442 throw new MojoExecutionException("Unable to build remote repository from " + object.toString(), e);
446 private String artifactToMvn(Artifact artifact) throws MojoExecutionException {
449 String groupId = artifact.getGroupId();
450 String artifactId = artifact.getArtifactId();
451 String version = artifact.getBaseVersion();
452 String type = artifact.getArtifactHandler().getExtension();
453 String classifier = artifact.getClassifier();
455 if (MavenUtil.isEmpty(classifier)) {
456 if ("jar".equals(type)) {
457 uri = String.format("mvn:%s/%s/%s", groupId, artifactId, version);
459 uri = String.format("mvn:%s/%s/%s/%s", groupId, artifactId, version, type);
462 uri = String.format("mvn:%s/%s/%s/%s/%s", groupId, artifactId, version, type, classifier);
467 private String[] toArray(List<String> strings) {
468 return strings.toArray(new String[strings.size()]);
471 private List<String> nonNullList(List<String> list) {
472 return list == null ? new ArrayList<String>() : list;