All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.karaf.tooling.AssemblyMojo Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.karaf.tooling;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Predicate;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.zip.ZipFile;

import org.apache.karaf.profile.assembly.Builder;
import org.apache.karaf.tooling.utils.IoUtils;
import org.apache.karaf.tooling.utils.MavenUtil;
import org.apache.karaf.tooling.utils.MojoSupport;
import org.apache.karaf.tooling.utils.ReactorMavenResolver;
import org.apache.karaf.tools.utils.model.KarafPropertyEdits;
import org.apache.karaf.tools.utils.model.io.stax.KarafPropertyInstructionsModelStaxReader;
import org.apache.karaf.util.Version;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
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.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.repository.WorkspaceReader;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.FrameworkFactory;

import static java.util.stream.Collectors.toList;

/**
 * Creates a customized Karaf distribution by installing features and setting up
 * configuration files.
 *
 * 

The plugin gets features from feature.xml files and KAR * archives declared as dependencies or as files configured with the * [startup|boot|installed]Respositories parameters. It picks up other files, such as config files, * from ${project.build.directory}/classes. Thus, a file in src/main/resources/etc * will be copied by the resource plugin to ${project.build.directory}/classes/etc, * and then added to the assembly by this goal. */ @Mojo(name = "assembly", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true) public class AssemblyMojo extends MojoSupport { /** * Base directory used to overwrite resources in generated assembly after the build (resource directory). */ @Parameter(defaultValue = "${project.basedir}/src/main/resources/assembly") protected File sourceDirectory; /** * Base directory used to copy the resources during the build (working directory). */ @Parameter(defaultValue = "${project.build.directory}/assembly") protected File workDirectory; /** * Optional location for custom features processing XML configuration * (etc/org.apache.karaf.features.cfg) */ @Parameter protected File featuresProcessing; /** * If greater than 0, the feature resolver concurrency, otherwise it defaults to the machine one. */ @Parameter protected int resolverParallelism; /* * There are three builder stages related to maven dependency scopes: * - Stage.Startup : scope=compile * - Stage.Boot : scope=runtime * - Stage.Installed : scope=provided * There's special category not related to stage - Blacklisted * * There are five kinds of artifacts/dependencies that may go into any of the above stages/categories/scopes: * - kars: maven artifacts with "kar" type * - repositories: maven artifacts with "features" classifier * - features: Karaf feature names (name[/version]) * - bundles: maven artifacts with "jar" or "bundle" type * - profiles: directories with Karaf 4 profiles * (Not all artifacts/dependencies may be connected with every stage/category/scope.) * * Blacklisting: * - kars: there are no blacklisted kars * - repositories: won't be processed at all (also affects transitive repositories) * - features: will be removed from JAXB model of features XML after loading * - bundles: will be removed from features of JAXB model after loading * - profiles: will be removed * * Stage.Startup: * - bundles: will be put to etc/startup.properties * - features: their bundles will be put to etc/startup.properties * - repositories: will be used to resolve startup bundles/feature before adding them to etc/startup.properties * - kars: unpacked to assembly, detected features XML repositories added as Stage.Startup repositories * * Stage.Boot: * - bundles: special etc/.xml features XML file will be created with feature. * etc/org.apacha.karaf.features.cfg will have this features XML file in featuresRepositories property and * the feature itself in featuresBoot property * - features: will be added to etc/org.apacha.karaf.features.cfg file, featuresBoot property * also features from Stage.Startup will be used here. * - repositories: will be added to etc/org.apacha.karaf.features.cfg file, featuresRepositories property * also repositories from Stage.Startup will be used here. * - kars: unpacked to assembly, detected features XML repositories added as Stage.Boot repositories * * Stage.Installed: * - bundles: will be copied to system/ * - features: their bundles and config files will be copied to system/ * - repositories: will be used to find Stage.Installed features * also repositories from Stage.Boot will be searched for Stage.Installed features * - kars: unpacked to assembly, detected features XML repositories added as Stage.Installed repositories */ /** * For given stage (startup, boot, install) if there are no stage-specific features and profiles, all features * from stage-specific repositories will be used. */ @Parameter(defaultValue = "true") protected boolean installAllFeaturesByDefault = true; /** * An environment identifier that may be used to select different variant of PID configuration file, e.g., * org.ops4j.pax.url.mvn.cfg#docker. */ @Parameter private String environment; /** * Default start level for bundles in features that don't specify it. */ @Parameter protected int defaultStartLevel = 30; /** * List of additional allowed protocols on bundles location URI */ @Parameter private List extraProtocols; /** * List of compile-scope features XML files to be used in startup stage (etc/startup.properties) */ @Parameter private List startupRepositories; /** * List of runtime-scope features XML files to be used in boot stage (etc/org.apache.karaf.features.cfg) */ @Parameter private List bootRepositories; /** * List of provided-scope features XML files to be used in install stage */ @Parameter private List installedRepositories; /** * List of blacklisted repository URIs. Blacklisted URI may use globs and version ranges. See * {@link org.apache.karaf.features.LocationPattern}. */ @Parameter private List blacklistedRepositories; /** * List of features from compile-scope features XML files and KARs to be installed into system repo * and listed in etc/startup.properties. */ @Parameter private List startupFeatures; /** * List of features from runtime-scope features XML files and KARs to be installed into system repo * and listed in featuresBoot property in etc/org.apache.karaf.features.cfg */ @Parameter private List bootFeatures; /** * List of features from runtime-scope features XML files and KARs to be installed into system repo * and listed in featuresBoot property in etc/org.apache.karaf.features.cfg. These will be installed * before {@link this#bootFeatures}. They will be wrapped in parentheses in featuresBoot. */ @Parameter private List firstStageBootFeatures; /** * List of features from provided-scope features XML files and KARs to be installed into system repo * and not mentioned elsewhere. */ @Parameter private List installedFeatures; /** *

List of feature blacklisting clauses. Each clause is in one of the formats ({@link org.apache.karaf.features.FeaturePattern}):

    *
  • feature-name
  • *
  • feature-name;range=version-or-range
  • *
  • feature-name/version-or-range
  • *

*/ @Parameter private List blacklistedFeatures; /** * List of compile-scope bundles added to etc/startup.properties */ @Parameter private List startupBundles; /** * List of runtime-scope bundles wrapped in special feature added to featuresBoot property * in etc/org.apache.karaf.features.cfg */ @Parameter private List bootBundles; /** * List of provided-scope bundles added to system repo */ @Parameter private List installedBundles; /** * List of blacklisted bundle URIs. Blacklisted URI may use globs and version ranges. See * {@link org.apache.karaf.features.LocationPattern}. */ @Parameter private List blacklistedBundles; /** * List of profile URIs to use */ @Parameter private List profilesUris; /** * List of profiles names to load from configured profilesUris and use as startup profiles. */ @Parameter private List startupProfiles; /** * List of profiles names to load from configured profilesUris and use as boot profiles. */ @Parameter private List bootProfiles; /** * List of profiles names to load from configured profilesUris and use as installed profiles. */ @Parameter private List installedProfiles; /** * List of blacklisted profile names (possibly using * glob) */ @Parameter private List blacklistedProfiles; /** * When assembly custom distribution, we can include generated and added profiles in the distribution itself, * in ${karaf.etc}/profiles directory. */ @Parameter(defaultValue = "false") private boolean writeProfiles; /** * When assembly custom distribution, we can also generate an XML/XSLT report with the summary of bundles. * This parameter specifies target directory, to which bundle-report.xml and bundle-report-full.xml * (along with XSLT stylesheet) will be written. */ @Parameter private String generateConsistencyReport; /** * When generating consistency report, we can specify project name. By default it's "Apache Karaf" */ @Parameter(defaultValue = "Apache Karaf") private String consistencyReportProjectName; /** * When generating consistency report, we can specify project version. By default it's "${project.version}" */ @Parameter(defaultValue = "${project.version}") private String consistencyReportProjectVersion; /* * KARs are not configured using Maven plugin configuration, but rather detected from dependencies. * All KARs are just unzipped into the assembly being constructed, but additionally KAR's embedded * features XML repositories are added to relevant stage. */ private List startupKars = new ArrayList<>(); private List bootKars = new ArrayList<>(); private List installedKars = new ArrayList<>(); /** * TODOCUMENT */ @Parameter private Builder.BlacklistPolicy blacklistPolicy = Builder.BlacklistPolicy.Discard; /** * Ignore the dependency attribute (dependency="[true|false]") on bundles, effectively forcing their * installation. */ @Parameter(defaultValue = "false") protected boolean ignoreDependencyFlag; /** *

Additional libraries to add into assembled distribution. Libraries are specified using * name[;url:=<url>][;type:=<type>][;export:=true|false][;delegate:=true|false] * syntax. If there's no url header directive, name is used as URI. Otherwise * name is used as target file name to use. * *

type may be:

    *
  • endorsed - library will be added to ${karaf.home}/lib/endorsed
  • *
  • extension - library will be added to ${karaf.home}/lib/ext
  • *
  • boot - library will be added to ${karaf.home}/lib/boot
  • *
  • by default, library is put directly into ${karaf.home}/lib - these libraries will * be used in default classloader for OSGi framework which will load {@link FrameworkFactory} implementation.
  • *
* *

export flag determines whether packages from Export-Package manifest * header of the library will be added to org.osgi.framework.system.packages.extra property in * ${karaf.etc}/config.properties. * *

delegate flag determines whether packages from Export-Pavkage manifest * header of the library will be added to org.osgi.framework.bootdelegation property in * ${karaf.etc}/config.properties. */ @Parameter protected List libraries; /** * Use reference:file:gr/oup/Id/artifactId/version/artifactId-version-classifier.type style * urls in etc/startup.properties. */ // see: // - org.apache.felix.framework.cache.BundleArchive.createRevisionFromLocation() // - org.apache.karaf.main.Main.installAndStartBundles() @Parameter(defaultValue = "false") protected boolean useReferenceUrls; /** * Include project build output directory in the assembly. This allows (filtered or unfiltered) Maven * resources directories to be used to provide additional resources in the assembly. */ @Parameter(defaultValue = "true") protected boolean includeBuildOutputDirectory; /** * Karaf version changes the way some configuration files are prepared (to adjust to given Karaf version * requirements). */ @Parameter protected Builder.KarafVersion karafVersion = Builder.KarafVersion.v4x; /** * Specify the version of Java SE to be assumed for osgi.ee. The value will be used in * etc/config.properties file, in java.specification.version placeholder used in * several properties:

    *
  • org.osgi.framework.system.packages
  • *
  • org.osgi.framework.system.capabilities
  • *
*

Valid values are: 1.6, 1.7, 1.8, 9 */ @Parameter(defaultValue = "1.8") protected String javase; /** * Specify which framework to use * (one of framework, framework-logback, static-framework, static-framework-logback, custom). */ @Parameter protected String framework; /** * Specify an XML file that instructs this goal to apply edits to * one or more standard Karaf property files. * The contents of this file are documented in detail on * this page. * This allows you to * customize these files without making copies in your resources * directories. Here's a simple example: *

     * {@literal
      
         
          
            config.properties
            put
            karaf.framework
            equinox
          
          
            config.properties
            extend
            org.osgi.framework.system.capabilities
            my-magic-capability
          
          
            config.properties
            extend
            some-other-list
            my-value-goes-first
            
         
      
    }
     
*/ @Parameter(defaultValue = "${project.basedir}/src/main/karaf/assembly-property-edits.xml") protected String propertyFileEdits; @Parameter protected KarafPropertyEdits propertyEdits; /** * Glob specifying which configuration PIDs in the selected boot features * should be extracted to ${karaf.etc} directory. By default all PIDs are extracted. */ @Parameter protected List pidsToExtract = Collections.singletonList("*"); /** * Specify a set of translated urls to use instead of downloading the artifacts * from their original locations. The given set will be extended with already * built artifacts from the maven project. */ @Parameter protected Properties translatedUrls; /** * Specify a list of additional properties that should be added to ${karaf.etc}/config.properties */ @Parameter protected Map config; /** * Specify a list of additional properties that should be added to ${karaf.etc}/system.properties */ @Parameter protected Map system; /** * List of files to delete from the source assembly. * Note that it is done after the target assembly is done so it can also remove side effects of the configuration. */ @Parameter protected List filesToRemove; @Component(role = WorkspaceReader.class, hint = "reactor") protected WorkspaceReader reactor; @Override public void execute() throws MojoExecutionException, MojoFailureException { try { setNullListsToEmpty(); setNullMapsToEmpty(); doExecute(); } catch (MojoExecutionException | MojoFailureException e) { throw e; } catch (Exception e) { throw new MojoExecutionException("Unable to build assembly", e); } } /** * Main processing method. Most of the work involves configuring and invoking {@link Builder a profile builder}. */ protected void doExecute() throws Exception { if (!startupProfiles.isEmpty() || !bootProfiles.isEmpty() || !installedProfiles.isEmpty()) { if (profilesUris.size() == 0) { throw new IllegalArgumentException("profilesUris option must be specified"); } } Builder builder = Builder.newInstance(); // Set up miscellaneous options builder.extraProtocols(extraProtocols); builder.offline(mavenSession.isOffline()); builder.localRepository(localRepo.getBasedir()); builder.resolverWrapper((resolver) -> new ReactorMavenResolver(reactor, resolver)); builder.javase(javase); builder.karafVersion(karafVersion); builder.useReferenceUrls(useReferenceUrls); builder.defaultAddAll(installAllFeaturesByDefault); builder.ignoreDependencyFlag(ignoreDependencyFlag); builder.propertyEdits(configurePropertyEdits()); builder.translatedUrls(configureTranslatedUrls()); builder.pidsToExtract(pidsToExtract); builder.writeProfiles(writeProfiles); builder.generateConsistencyReport(generateConsistencyReport); builder.setConsistencyReportProjectName(consistencyReportProjectName); builder.setConsistencyReportProjectVersion(consistencyReportProjectVersion); builder.environment(environment); builder.defaultStartLevel(defaultStartLevel); if (featuresProcessing != null) { builder.setFeaturesProcessing(featuresProcessing.toPath()); } if (resolverParallelism > 0) { builder.resolverParallelism(resolverParallelism); } // Set up remote repositories from Maven build, to be used by pax-url-aether resolver String remoteRepositories = MavenUtil.remoteRepositoryList(project.getRemoteProjectRepositories()); getLog().info("Using repositories:"); for (String r : remoteRepositories.split(",")) { getLog().info(" " + r); } builder.mavenRepositories(remoteRepositories); // Set up config and system properties config.forEach(builder::config); system.forEach(builder::system); // Set up blacklisted items builder.blacklistBundles(blacklistedBundles); builder.blacklistFeatures(blacklistedFeatures); builder.blacklistProfiles(blacklistedProfiles); builder.blacklistRepositories(blacklistedRepositories); builder.blacklistPolicy(blacklistPolicy); // Creating system directory configureWorkDirectory(); getLog().info("Creating work directory: " + workDirectory); builder.homeDirectory(workDirectory.toPath()); // Loading KARs and features repositories getLog().info("Loading direct KAR and features XML dependencies"); processDirectMavenDependencies(); // Set up profiles and libraries profilesUris.forEach(builder::profilesUris); libraries.forEach(builder::libraries); // Startup stage detectStartupKarsAndFeatures(builder); builder.defaultStage(Builder.Stage.Startup) .kars(toArray(startupKars)) .repositories(startupFeatures.isEmpty() && startupProfiles.isEmpty() && installAllFeaturesByDefault, toArray(startupRepositories)) .features(toArray(startupFeatures)) .bundles(toArray(startupBundles)) .profiles(toArray(startupProfiles)); // Installed stage builder.defaultStage(Builder.Stage.Installed) .kars(toArray(installedKars)) .repositories(installedFeatures.isEmpty() && installedProfiles.isEmpty() && installAllFeaturesByDefault, toArray(installedRepositories)) .features(toArray(installedFeatures)) .bundles(toArray(installedBundles)) .profiles(toArray(installedProfiles)); // Boot stage builder.defaultStage(Builder.Stage.Boot) .kars(toArray(bootKars)) .repositories(bootFeatures.isEmpty() && bootProfiles.isEmpty() && installAllFeaturesByDefault, toArray(bootRepositories)) .firstStageBootFeatures(toArray(firstStageBootFeatures)) .features(toArray(bootFeatures)) .bundles(toArray(bootBundles)) .profiles(toArray(bootProfiles)); // Generate the assembly builder.generateAssembly(); // Include project classes content if not specified otherwise if (includeBuildOutputDirectory) IoUtils.copyDirectory(new File(project.getBuild().getOutputDirectory()), workDirectory); // Overwrite assembly dir contents with source directory (not filtered) when directory exists if (sourceDirectory.exists()) IoUtils.copyDirectory(sourceDirectory, workDirectory); // Chmod the bin/* scripts File[] files = new File(workDirectory, "bin").listFiles(); if (files != null) { for (File file : files) { if (!file.getName().endsWith(".bat")) { try { Files.setPosixFilePermissions(file.toPath(), PosixFilePermissions.fromString("rwxr-xr-x")); } catch (Throwable ignore) { // we tried our best, perhaps the OS does not support POSIX file perms. } } } } if (filesToRemove != null) { final Path base = workDirectory.toPath(); filesToRemove.forEach(toDrop -> { final int lastSep = Math.max(toDrop.lastIndexOf('/'), toDrop.lastIndexOf(File.separatorChar)); final boolean startsWithDir = toDrop.contains(File.separator) || toDrop.contains("/"); final String name = !startsWithDir ? toDrop : toDrop.substring(lastSep + 1); final Path dir = !startsWithDir ? base : base.resolve(toDrop.substring(0, lastSep)); final int wildcard = name.lastIndexOf('*'); final Predicate matcher; if (wildcard >= 0) { final String suffix = name.substring(wildcard + 1); final String prefix = name.substring(0, wildcard); matcher = n -> n.startsWith(prefix) && n.endsWith(suffix); } else { // we likely bet this case will not happen often (to ignore the version at least) // so we don't optimize this branch by deleting directly the file matcher = name::equals; } try { final List toDelete = Files.list(dir) .filter(it -> matcher.test(it.getFileName().toString())) .collect(toList()); if (toDelete.isEmpty()) { getLog().info("File deletion '" + toDrop + "' ignored (not found)"); } else { toDelete.stream().peek(it -> getLog().info("Deleting '" + base.relativize(it) + "'")).forEach(it -> { try { if (Files.isDirectory(it)) { IoUtils.deleteRecursive(it.toFile()); } else { Files.delete(it); } } catch (final IOException e) { throw new IllegalStateException(e); } }); } } catch (final IOException e) { throw new IllegalStateException(e); } }); } } private void configureWorkDirectory() { IoUtils.deleteRecursive(workDirectory); workDirectory.mkdirs(); new File(workDirectory, "etc").mkdirs(); new File(workDirectory, "system").mkdirs(); } /** *

Turns direct maven dependencies into startup/boot/installed artifacts.

*

{@link MavenProject#getDependencyArtifacts()} is deprecated, but we don't want (?) transitive * dependencies given by {@link MavenProject#getArtifacts()}.

*/ @SuppressWarnings("deprecation") private void processDirectMavenDependencies() { for (Artifact artifact : project.getDependencyArtifacts()) { Builder.Stage stage = Builder.Stage.fromMavenScope(artifact.getScope()); if (stage == null) { continue; } String uri = artifactToMvn(artifact); switch (getType(artifact)) { case "kar": addUris(stage, uri, startupKars, bootKars, installedKars); break; case "features": addUris(stage, uri, startupRepositories, bootRepositories, installedRepositories); break; case "bundle": addUris(stage, uri, startupBundles, bootBundles, installedBundles); break; } } } private void addUris(Builder.Stage stage, String uri, List startup, List boot, List installed) { switch (stage) { case Startup: startup.add(uri); break; case Boot: boot.add(uri); break; case Installed: installed.add(uri); break; } } /** *

Custom distribution is created from at least one startup KAR and one startup * feature. Such startup KAR + feature is called framework.

* *

We can specify one of 5 frameworks:

    *
  • framework: mvn:org.apache.karaf.features/framework/VERSION/kar and framework feature
  • *
  • framework-logback: mvn:org.apache.karaf.features/framework/VERSION/kar and framework-logback feature
  • *
  • static-framework: mvn:org.apache.karaf.features/static/VERSION/kar and static-framework feature
  • *
  • static-framework-logback: mvn:org.apache.karaf.features/static/VERSION/kar and static-framework-logback feature
  • *
  • custom: both startup KAR and startup feature has to be specified explicitly
  • *

* @param builder */ private void detectStartupKarsAndFeatures(Builder builder) { boolean hasStandardKarafFrameworkKar = false; boolean hasCustomFrameworkKar = false; for (Iterator iterator = startupKars.iterator(); iterator.hasNext(); ) { String kar = iterator.next(); if (kar.startsWith("mvn:org.apache.karaf.features/framework/") || kar.startsWith("mvn:org.apache.karaf.features/static/")) { hasStandardKarafFrameworkKar = true; iterator.remove(); if (framework == null) { framework = kar.startsWith("mvn:org.apache.karaf.features/framework/") ? "framework" : "static-framework"; } getLog().info(" Standard startup Karaf KAR found: " + kar); builder.kars(Builder.Stage.Startup, false, kar); break; } } if (!hasStandardKarafFrameworkKar) { if ("custom".equals(framework)) { // we didn't detect standard Karaf KAR (framework or static), so we expect at least one // other KAR dependency with compile scope and at least one startup feature if (startupKars.isEmpty()) { throw new IllegalArgumentException("Custom KAR was declared, but there's no Maven dependency with type=kar and scope=compile." + " Please specify at least one KAR for custom assembly."); } if (startupFeatures.isEmpty()) { throw new IllegalArgumentException("Custom KAR was declared, but there's no startup feature declared." + " Please specify at least one startup feature defined in features XML repository inside custom startup KAR or startup repository."); } hasCustomFrameworkKar = true; for (String startupKar : startupKars) { getLog().info(" Custom startup KAR found: " + startupKar); } } else if (framework == null) { throw new IllegalArgumentException("Can't determine framework to use (framework, framework-logback, static-framework, static-framework-logback, custom)." + " Please specify valid \"framework\" option or add Maven dependency with \"kar\" type and \"compile\" scope for one of standard Karaf KARs."); } else { String realKarafVersion = Version.karafVersion(); String kar; switch (framework) { case "framework": case "framework-logback": kar = "mvn:org.apache.karaf.features/framework/" + realKarafVersion + "/kar"; break; case "static-framework": case "static-framework-logback": kar = "mvn:org.apache.karaf.features/static/" + realKarafVersion + "/kar"; break; default: throw new IllegalArgumentException("Unsupported framework: " + framework); } getLog().info(" Standard startup KAR implied from framework (" + framework + "): " + kar); builder.kars(Builder.Stage.Startup, false, kar); } } if (hasStandardKarafFrameworkKar && !startupFeatures.contains(framework)) { getLog().info(" Feature " + framework + " will be added as a startup feature"); builder.features(Builder.Stage.Startup, framework); } } private KarafPropertyEdits configurePropertyEdits() throws IOException, XMLStreamException { KarafPropertyEdits edits = null; if (propertyFileEdits != null) { File file = new File(propertyFileEdits); if (file.exists()) { try (InputStream editsStream = new FileInputStream(propertyFileEdits)) { KarafPropertyInstructionsModelStaxReader kipmsr = new KarafPropertyInstructionsModelStaxReader(); edits = kipmsr.read(editsStream, true); } } } if (edits == null && propertyEdits != null) { edits = propertyEdits; } else if (edits != null && propertyEdits != null && !propertyEdits.getEdits().isEmpty()) { edits.getEdits().addAll(propertyEdits.getEdits()); } return edits; } private Map configureTranslatedUrls() { Map urls = new HashMap<>(); List artifacts = new ArrayList<>(project.getAttachedArtifacts()); artifacts.add(project.getArtifact()); for (Artifact artifact : artifacts) { if (artifact.getFile() != null && artifact.getFile().exists()) { String mvnUrl = artifactToMvn(artifact); urls.put(mvnUrl, artifact.getFile().toURI().toString()); } } for (Map.Entry entry : translatedUrls.entrySet()) { urls.put(entry.getKey().toString(), entry.getValue().toString()); } return urls; } private String getType(Artifact artifact) { // Identify kars if ("kar".equals(artifact.getType())) { return "kar"; } if ("zip".equals(artifact.getType())) { try (ZipFile zip = new ZipFile(artifact.getFile())) { if (zip.getEntry("META-INF/KARAF.MF") != null) { return "kar"; } } catch (IOException e) { // Ignore } } // Identify features if ("features".equals(artifact.getClassifier())) { return "features"; } if ("xml".equals(artifact.getType())) { try (InputStream is = new FileInputStream(artifact.getFile())) { XMLInputFactory xif = XMLInputFactory.newFactory(); xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true); xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); XMLStreamReader r = xif.createXMLStreamReader(is); r.nextTag(); QName name = r.getName(); if (name.getLocalPart().equals("features") && (name.getNamespaceURI().isEmpty() || name.getNamespaceURI().startsWith("http://karaf.apache.org/xmlns/features/"))) { return "features"; } } catch (Exception e) { // Ignore } } // Identify bundles if ("bundle".equals(artifact.getType())) { return "bundle"; } if ("jar".equals(artifact.getType())) { try (JarFile jar = new JarFile(artifact.getFile())) { Manifest manifest = jar.getManifest(); if (manifest != null && manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME) != null) { return "bundle"; } } catch (Exception e) { // Ignore } } return "unknown"; } private String artifactToMvn(Artifact artifact) { String uri; String groupId = artifact.getGroupId(); String artifactId = artifact.getArtifactId(); String version = artifact.getBaseVersion(); String type = artifact.getArtifactHandler().getExtension(); String classifier = artifact.getClassifier(); if (MavenUtil.isEmpty(classifier)) { if ("jar".equals(type)) { uri = String.format("mvn:%s/%s/%s", groupId, artifactId, version); } else { uri = String.format("mvn:%s/%s/%s/%s", groupId, artifactId, version, type); } } else { uri = String.format("mvn:%s/%s/%s/%s/%s", groupId, artifactId, version, type, classifier); } return uri; } private String[] toArray(List strings) { return strings.toArray(new String[strings.size()]); } private void setNullListsToEmpty() { startupRepositories = nonNullList(startupRepositories); bootRepositories = nonNullList(bootRepositories); installedRepositories = nonNullList(installedRepositories); blacklistedRepositories = nonNullList(blacklistedRepositories); extraProtocols = nonNullList(extraProtocols); startupBundles = nonNullList(startupBundles); bootBundles = nonNullList(bootBundles); installedBundles = nonNullList(installedBundles); blacklistedBundles = nonNullList(blacklistedBundles); startupFeatures = nonNullList(startupFeatures); bootFeatures = nonNullList(bootFeatures); firstStageBootFeatures = nonNullList(firstStageBootFeatures); installedFeatures = nonNullList(installedFeatures); blacklistedFeatures = nonNullList(blacklistedFeatures); startupProfiles = nonNullList(startupProfiles); bootProfiles = nonNullList(bootProfiles); installedProfiles = nonNullList(installedProfiles); blacklistedProfiles = nonNullList(blacklistedProfiles); libraries = nonNullList(libraries); profilesUris = nonNullList(profilesUris); } private void setNullMapsToEmpty() { config = nonNullMap(config); system = nonNullMap(system); translatedUrls = nonNullProps(translatedUrls); } private List nonNullList(List list) { final List nonNullList = list == null ? new ArrayList<>() : list; return nonNullList.stream().filter(Objects::nonNull).collect(Collectors.toList()); } private Map nonNullMap(Map map) { return map == null ? new LinkedHashMap<>() : map; } private Properties nonNullProps(Properties props) { return props == null ? new Properties() : props; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy