| /* |
| * 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 java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.atomic.AtomicLong; |
| import java.util.jar.Attributes; |
| import java.util.jar.Manifest; |
| import java.util.regex.Pattern; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipInputStream; |
| |
| import aQute.bnd.osgi.Macro; |
| import aQute.bnd.osgi.Processor; |
| import org.apache.felix.resolver.Logger; |
| import org.apache.felix.resolver.ResolverImpl; |
| import org.apache.felix.utils.resource.ResourceBuilder; |
| import org.apache.felix.utils.resource.ResourceImpl; |
| import org.apache.felix.utils.version.VersionRange; |
| import org.apache.felix.utils.version.VersionTable; |
| import org.apache.karaf.features.BundleInfo; |
| import org.apache.karaf.features.DeploymentEvent; |
| import org.apache.karaf.features.FeatureEvent; |
| import org.apache.karaf.features.FeaturesService; |
| import org.apache.karaf.features.LocationPattern; |
| import org.apache.karaf.features.internal.download.DownloadCallback; |
| import org.apache.karaf.features.internal.download.DownloadManager; |
| import org.apache.karaf.features.internal.download.Downloader; |
| import org.apache.karaf.features.internal.download.StreamProvider; |
| import org.apache.karaf.features.internal.model.Conditional; |
| import org.apache.karaf.features.internal.model.ConfigFile; |
| import org.apache.karaf.features.internal.model.Feature; |
| import org.apache.karaf.features.internal.model.Features; |
| import org.apache.karaf.features.internal.model.JaxbUtil; |
| import org.apache.karaf.features.internal.resolver.ResourceUtils; |
| import org.apache.karaf.features.internal.service.Deployer; |
| import org.apache.karaf.features.internal.service.FeaturesProcessorImpl; |
| import org.apache.karaf.features.internal.service.FeaturesServiceConfig; |
| import org.apache.karaf.features.internal.service.State; |
| import org.apache.karaf.features.internal.service.StaticInstallSupport; |
| import org.apache.karaf.features.internal.util.MapUtils; |
| import org.apache.karaf.features.internal.util.MultiException; |
| import org.apache.karaf.profile.assembly.CustomDownloadManager; |
| 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.util.config.PropertiesLoader; |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugins.annotations.Component; |
| 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.ops4j.pax.url.mvn.MavenResolver; |
| import org.ops4j.pax.url.mvn.MavenResolvers; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.Version; |
| import org.osgi.framework.namespace.IdentityNamespace; |
| import org.osgi.framework.startlevel.BundleStartLevel; |
| import org.osgi.framework.wiring.BundleCapability; |
| import org.osgi.framework.wiring.BundleRequirement; |
| import org.osgi.framework.wiring.BundleRevision; |
| import org.osgi.framework.wiring.BundleWiring; |
| import org.osgi.resource.Requirement; |
| import org.osgi.service.resolver.ResolutionException; |
| |
| import static java.util.jar.JarFile.MANIFEST_NAME; |
| |
| @Mojo(name = "verify", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, threadSafe = true) |
| public class VerifyMojo extends MojoSupport { |
| |
| @Parameter(property = "descriptors") |
| protected Set<String> descriptors; |
| |
| @Parameter(property = "blacklistedDescriptors") |
| protected Set<String> blacklistedDescriptors; |
| |
| @Parameter(property = "featureProcessingInstructions") |
| protected File featureProcessingInstructions; |
| |
| @Parameter(property = "features") |
| protected List<String> features; |
| |
| @Parameter(property = "framework") |
| protected Set<String> framework; |
| |
| @Parameter(property = "configuration") |
| protected String configuration; |
| |
| @Parameter(property = "distribution", defaultValue = "org.apache.karaf:apache-karaf") |
| protected String distribution; |
| |
| @Parameter(property = "javase") |
| protected String javase; |
| |
| @Parameter(property = "dist-dir") |
| protected String distDir; |
| |
| @Parameter(property = "karaf-version") |
| protected String karafVersion; |
| |
| @Parameter(property = "additional-metadata") |
| protected File additionalMetadata; |
| |
| @Parameter(property = "ignore-missing-conditions") |
| protected boolean ignoreMissingConditions; |
| |
| @Parameter(property = "fail") |
| protected String fail = "end"; |
| |
| @Parameter(property = "verify-transitive") |
| protected boolean verifyTransitive = false; |
| |
| @Parameter(defaultValue = "${project}", readonly = true) |
| protected MavenProject project; |
| |
| @Component(role = WorkspaceReader.class, hint = "reactor") |
| protected WorkspaceReader reactor; |
| |
| @Parameter(property = "skip", defaultValue = "${features.verify.skip}") |
| protected boolean skip; |
| |
| @Parameter(readonly = true, defaultValue = "${project.groupId}") |
| protected String selfGroupId; |
| |
| @Parameter(readonly = true, defaultValue = "${project.artifactId}") |
| protected String selfArtifactId; |
| |
| protected MavenResolver resolver; |
| |
| @Override |
| public void execute() throws MojoExecutionException { |
| if (skip) { |
| return; |
| } |
| |
| if (karafVersion == null) { |
| karafVersion = org.apache.karaf.util.Version.karafVersion(); |
| } |
| |
| Hashtable<String, String> config = new Hashtable<>(); |
| String remoteRepositories = MavenUtil.remoteRepositoryList(project.getRemoteProjectRepositories()); |
| getLog().info("Using repositories: " + remoteRepositories); |
| config.put("maven.repositories", remoteRepositories); |
| config.put("maven.localRepository", localRepo.getBasedir()); |
| |
| if (mavenSession.getRequest().getUserSettingsFile().exists()) { |
| config.put("maven.settings", mavenSession.getRequest().getUserSettingsFile().toString()); |
| } |
| |
| // TODO: add more configuration bits ? |
| resolver = new ReactorMavenResolver(reactor, MavenResolvers.createMavenResolver(config, "maven")); |
| doExecute(); |
| } |
| |
| private String getVersion(String id, String def) { |
| String v = getVersion(id); |
| return v != null ? v : def; |
| } |
| |
| private String getVersion(String id) { |
| Artifact artifact = project.getArtifactMap().get(id); |
| if (artifact != null) { |
| return artifact.getBaseVersion(); |
| } else if (id.startsWith("org.apache.karaf")) { |
| return karafVersion; |
| } else { |
| return null; |
| } |
| } |
| |
| private static Object invoke(Object object, String getter) throws MojoExecutionException { |
| try { |
| return object.getClass().getMethod(getter).invoke(object); |
| } catch (Exception e) { |
| throw new MojoExecutionException("Unable to build remote repository from " + object.toString(), e); |
| } |
| } |
| |
| private static Object getPolicy(Object object, boolean snapshots) throws MojoExecutionException { |
| return invoke(object, "getPolicy", new Class[] { Boolean.TYPE }, new Object[] { snapshots }); |
| } |
| |
| private static Object invoke(Object object, String getter, Class<?>[] types, Object[] params) throws MojoExecutionException { |
| try { |
| return object.getClass().getMethod(getter, types).invoke(object, params); |
| } catch (Exception e) { |
| throw new MojoExecutionException("Unable to build remote repository from " + object.toString(), e); |
| } |
| } |
| |
| protected void doExecute() throws MojoExecutionException { |
| System.setProperty("karaf.home", "target/karaf"); |
| System.setProperty("karaf.data", "target/karaf/data"); |
| |
| Hashtable<String, String> properties = new Hashtable<>(); |
| |
| if (additionalMetadata != null) { |
| try (Reader reader = new FileReader(additionalMetadata)) { |
| Properties metadata = new Properties(); |
| metadata.load(reader); |
| for (Enumeration<?> e = metadata.propertyNames(); e.hasMoreElements(); ) { |
| Object key = e.nextElement(); |
| Object val = metadata.get(key); |
| properties.put(key.toString(), val.toString()); |
| } |
| } catch (IOException e) { |
| throw new MojoExecutionException("Unable to load additional metadata from " + additionalMetadata, e); |
| } |
| } |
| |
| Set<String> allDescriptors = new LinkedHashSet<>(); |
| if (descriptors == null) { |
| if (framework == null) { |
| framework = Collections.singleton("framework"); |
| } |
| descriptors = new LinkedHashSet<>(); |
| if (framework.contains("framework")) { |
| allDescriptors.add("mvn:org.apache.karaf.features/framework/" + getVersion("org.apache.karaf.features:framework") + "/xml/features"); |
| } |
| String filePrefix = null; |
| if (System.getProperty("os.name").contains("Windows")) { |
| filePrefix = "file:/"; |
| } else { |
| filePrefix = "file:"; |
| } |
| allDescriptors.add(filePrefix + project.getBuild().getDirectory() + File.separator |
| + "feature" |
| + File.separator |
| + "feature.xml"); |
| } else { |
| allDescriptors.addAll(descriptors); |
| if (framework != null && framework.contains("framework")) { |
| allDescriptors.add("mvn:org.apache.karaf.features/framework/" + getVersion("org.apache.karaf.features:framework") + "/xml/features"); |
| } |
| } |
| |
| // TODO: allow using external configuration ? |
| ScheduledExecutorService executor = Executors.newScheduledThreadPool(8); |
| DownloadManager manager = new CustomDownloadManager(resolver, executor); |
| final Map<String, Features> repositories; |
| Map<String, List<Feature>> allFeatures = new HashMap<>(); |
| try { |
| repositories = loadRepositories(manager, allDescriptors); |
| for (String repoUri : repositories.keySet()) { |
| List<Feature> features = repositories.get(repoUri).getFeature(); |
| // Ack features to inline configuration files urls |
| for (Feature feature : features) { |
| for (org.apache.karaf.features.internal.model.Bundle bi : feature.getBundle()) { |
| String loc = bi.getLocation(); |
| String nloc = null; |
| if (loc.contains("file:")) { |
| for (ConfigFile cfi : feature.getConfigfile()) { |
| if (cfi.getFinalname().substring(1) |
| .equals(loc.substring(loc.indexOf("file:") + "file:".length()))) { |
| nloc = cfi.getLocation(); |
| } |
| } |
| } |
| if (nloc != null) { |
| Field field = bi.getClass().getDeclaredField("location"); |
| field.setAccessible(true); |
| field.set(bi, loc.substring(0, loc.indexOf("file:")) + nloc); |
| } |
| } |
| } |
| allFeatures.put(repoUri, features); |
| } |
| } catch (Exception e) { |
| throw new MojoExecutionException("Unable to load features descriptors", e); |
| } |
| |
| List<Feature> featuresToTest = new ArrayList<>(); |
| if (verifyTransitive) { |
| for (List<Feature> features : allFeatures.values()) { |
| featuresToTest.addAll(features); |
| } |
| } else { |
| for (String uri : descriptors) { |
| featuresToTest.addAll(allFeatures.get(uri)); |
| } |
| } |
| if (features != null && !features.isEmpty()) { |
| Pattern pattern = getPattern(features); |
| for (Iterator<Feature> iterator = featuresToTest.iterator(); iterator.hasNext();) { |
| Feature feature = iterator.next(); |
| String id = feature.getName() + "/" + feature.getVersion(); |
| if (!pattern.matcher(id).matches()) { |
| iterator.remove(); |
| } |
| } |
| } |
| |
| for (String fmk : framework) { |
| properties.put("feature.framework." + fmk, fmk); |
| } |
| Set<String> successes = new LinkedHashSet<>(); |
| Set<String> ignored = new LinkedHashSet<>(); |
| Set<String> skipped = new LinkedHashSet<>(); |
| Map<String, Exception> failures = new LinkedHashMap<>(); |
| for (Feature feature : featuresToTest) { |
| String id = feature.getId(); |
| if (feature.isBlacklisted()) { |
| skipped.add(id); |
| getLog().info("Verification of feature " + id + " skipped"); |
| continue; |
| } |
| try { |
| verifyResolution(new CustomDownloadManager(resolver, executor), |
| repositories, Collections.singleton(id), properties); |
| successes.add(id); |
| getLog().info("Verification of feature " + id + " succeeded"); |
| } catch (Exception e) { |
| if (e.getCause() instanceof ResolutionException || !getLog().isDebugEnabled()) { |
| getLog().warn(e.getMessage() + ": " + id); |
| getLog().warn(e.getCause().getMessage()); |
| } else { |
| getLog().warn(e); |
| } |
| failures.put(id, e); |
| if ("first".equals(fail)) { |
| throw e; |
| } |
| } |
| for (Conditional cond : feature.getConditional()) { |
| Set<String> ids = new LinkedHashSet<>(); |
| ids.add(feature.getId()); |
| ids.addAll(cond.getCondition()); |
| String cid = String.join("+", ids); |
| try { |
| verifyResolution(manager, repositories, ids, properties); |
| successes.add(cid); |
| getLog().info("Verification of feature " + cid + " succeeded"); |
| } catch (Exception e) { |
| if (ignoreMissingConditions && e.getCause() instanceof ResolutionException) { |
| boolean ignore = true; |
| Collection<Requirement> requirements = ((ResolutionException) e.getCause()).getUnresolvedRequirements(); |
| for (Requirement req : requirements) { |
| ignore &= (IdentityNamespace.IDENTITY_NAMESPACE.equals(req.getNamespace()) |
| && ResourceUtils.TYPE_FEATURE.equals(req.getAttributes().get("type")) |
| && cond.getCondition().contains(req.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE).toString())); |
| } |
| if (ignore) { |
| ignored.add(cid); |
| getLog().warn("Feature resolution failed for " + cid |
| + "\nMessage: " + e.getCause().getMessage()); |
| continue; |
| } |
| } |
| if (e.getCause() instanceof ResolutionException || !getLog().isDebugEnabled()) { |
| getLog().warn(e.getMessage()); |
| } else { |
| getLog().warn(e); |
| } |
| failures.put(cid, e); |
| if ("first".equals(fail)) { |
| throw e; |
| } |
| } |
| } |
| } |
| int nb = successes.size() + ignored.size() + failures.size(); |
| getLog().info("Features verified: " + nb + ", failures: " + failures.size() + ", ignored: " + ignored.size() + ", skipped: " + skipped.size()); |
| if (!failures.isEmpty()) { |
| getLog().info("Failures: " + String.join(", ", failures.keySet())); |
| } |
| if ("end".equals(fail) && !failures.isEmpty()) { |
| throw new MojoExecutionException("Verification failures", new MultiException("Verification failures", new ArrayList<>(failures.values()))); |
| } |
| } |
| |
| static Pattern getPattern(List<String> features) { |
| StringBuilder sb = new StringBuilder(); |
| boolean prevIsNeg = false; |
| for (String feature : features) { |
| if (sb.length() > 0 && !prevIsNeg) { |
| sb.append("|"); |
| } |
| sb.append("("); |
| feature = feature.trim(); |
| boolean negative = feature.startsWith("!"); |
| if (negative) { |
| feature = feature.substring("!".length()); |
| sb.append("(?!"); |
| } |
| String p = feature.replaceAll("\\.", "\\\\.").replaceAll("\\*", ".*"); |
| sb.append(p); |
| if (!feature.contains("/")) { |
| sb.append("/.*"); |
| } |
| if (negative) { |
| sb.append(")"); |
| } |
| prevIsNeg = negative; |
| } |
| for (String feature : features) { |
| sb.append(")"); |
| } |
| return Pattern.compile(sb.toString()); |
| } |
| |
| private void verifyResolution(DownloadManager manager, final Map<String, Features> repositories, Set<String> features, Hashtable<String, String> properties) throws MojoExecutionException { |
| try { |
| Bundle systemBundle = getSystemBundle(getMetadata(properties, "metadata#")); |
| DummyDeployCallback callback = new DummyDeployCallback(systemBundle, repositories.values()); |
| Deployer deployer = new Deployer(manager, new ResolverImpl(new MavenResolverLog()), callback); |
| |
| |
| // Install framework |
| Deployer.DeploymentRequest request = Deployer.DeploymentRequest.defaultDeploymentRequest(); |
| |
| for (String fmwk : framework) { |
| MapUtils.addToMapSet(request.requirements, FeaturesService.ROOT_REGION, fmwk); |
| } |
| try { |
| deployer.deploy(callback.getDeploymentState(), request); |
| } catch (Exception e) { |
| throw new MojoExecutionException("Unable to resolve framework features", e); |
| } |
| |
| /* |
| boolean resolveOptionalImports = getResolveOptionalImports(properties); |
| |
| DeploymentBuilder builder = new DeploymentBuilder( |
| manager, |
| null, |
| repositories.values(), |
| -1 // Disable url handlers |
| ); |
| Map<String, Resource> downloadedResources = builder.download( |
| getPrefixedProperties(properties, "feature."), |
| getPrefixedProperties(properties, "bundle."), |
| getPrefixedProperties(properties, "fab."), |
| getPrefixedProperties(properties, "req."), |
| getPrefixedProperties(properties, "override."), |
| getPrefixedProperties(properties, "optional."), |
| getMetadata(properties, "metadata#") |
| ); |
| |
| for (String uri : getPrefixedProperties(properties, "resources.")) { |
| builder.addResourceRepository(new MetadataRepository(new HttpMetadataProvider(uri))); |
| } |
| */ |
| |
| // Install features |
| for (String feature : features) { |
| MapUtils.addToMapSet(request.requirements, FeaturesService.ROOT_REGION, feature); |
| } |
| try { |
| deployer.deployFully(callback.getDeploymentState(), request); |
| |
| // TODO: find unused resources ? |
| } catch (Exception e) { |
| throw new MojoExecutionException("Feature resolution failed for " + features |
| + "\nMessage: " + (e instanceof ResolutionException ? e.getMessage() : e.toString()) |
| + "\nRepositories: " + toString(new TreeSet<>(repositories.keySet())) |
| + "\nResources: " + toString(new TreeSet<>(manager.getProviders().keySet())), e); |
| } |
| } catch (MojoExecutionException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new MojoExecutionException("Error verifying feature " + features + "\nMessage: " + e.getMessage(), e); |
| } |
| } |
| |
| private static String toString(Collection<String> collection) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("{\n"); |
| for (String s : collection) { |
| sb.append("\t").append(s).append("\n"); |
| } |
| sb.append("}"); |
| return sb.toString(); |
| } |
| |
| private Bundle getSystemBundle(Map<String, Map<VersionRange, Map<String, String>>> metadata) throws Exception { |
| URL configPropURL; |
| if (configuration != null) { |
| configPropURL = new URL(configuration); |
| } else { |
| Artifact karafDistro = project.getArtifactMap().get(distribution); |
| if (karafDistro != null) { |
| String dir = distDir; |
| if ("kar".equals(karafDistro.getType()) && dir == null) { |
| dir = "resources"; |
| } |
| if (dir == null) { |
| dir = karafDistro.getArtifactId() + "-" + karafDistro.getBaseVersion(); |
| } |
| configPropURL = new URL("jar:file:" + karafDistro.getFile() + "!/" + dir + "/etc/config.properties"); |
| } else { |
| String version = getVersion(distribution, "RELEASE"); |
| String[] dist = distribution.split(":"); |
| File distFile = resolver.resolve(dist[0], dist[1], null, "zip", version); |
| String resolvedVersion = distFile.getName().substring(dist[1].length() + 1, distFile.getName().length() - 4); |
| String dir = distDir; |
| if (dir == null) { |
| dir = dist[1] + "-" + resolvedVersion; |
| } |
| configPropURL = new URL("jar:file:" + distFile + "!/" + dir + "/etc/config.properties"); |
| } |
| } |
| org.apache.felix.utils.properties.Properties configProps = PropertiesLoader.loadPropertiesFile(configPropURL, true); |
| // copySystemProperties(configProps); |
| if (javase == null) { |
| configProps.put("java.specification.version", System.getProperty("java.specification.version")); |
| } else { |
| configProps.put("java.specification.version", javase); |
| } |
| configProps.substitute(); |
| |
| Attributes attributes = new Attributes(); |
| attributes.putValue(Constants.BUNDLE_MANIFESTVERSION, "2"); |
| attributes.putValue(Constants.BUNDLE_SYMBOLICNAME, "system.bundle"); |
| attributes.putValue(Constants.BUNDLE_VERSION, "0.0.0"); |
| |
| String exportPackages = configProps.getProperty("org.osgi.framework.system.packages"); |
| if (configProps.containsKey("org.osgi.framework.system.packages.extra")) { |
| exportPackages += "," + configProps.getProperty("org.osgi.framework.system.packages.extra"); |
| } |
| exportPackages = exportPackages.replaceAll(",\\s*,", ","); |
| attributes.putValue(Constants.EXPORT_PACKAGE, exportPackages); |
| |
| String systemCaps = configProps.getProperty("org.osgi.framework.system.capabilities"); |
| attributes.putValue(Constants.PROVIDE_CAPABILITY, systemCaps); |
| |
| // TODO: support metadata overrides on system bundle |
| // attributes = DeploymentBuilder.overrideAttributes(attributes, metadata); |
| |
| final Hashtable<String, String> headers = new Hashtable<>(); |
| for (Map.Entry<Object, Object> attr : attributes.entrySet()) { |
| headers.put(attr.getKey().toString(), attr.getValue().toString()); |
| } |
| |
| final FakeBundleRevision resource = new FakeBundleRevision(headers, "system-bundle", 0l); |
| return resource.getBundle(); |
| } |
| |
| |
| public Map<String, Features> loadRepositories(DownloadManager manager, Set<String> uris) throws Exception { |
| final Map<String, Features> loaded = new HashMap<>(); |
| final Downloader downloader = manager.createDownloader(); |
| |
| FeaturesServiceConfig config = null; |
| if (featureProcessingInstructions != null) { |
| config = new FeaturesServiceConfig(featureProcessingInstructions.toURI().toString(), null); |
| } else { |
| config = new FeaturesServiceConfig(); |
| } |
| FeaturesProcessorImpl processor = new FeaturesProcessorImpl(config); |
| if (blacklistedDescriptors != null) { |
| blacklistedDescriptors.forEach(lp -> processor.getInstructions().getBlacklistedRepositoryLocationPatterns() |
| .add(new LocationPattern(lp))); |
| } |
| processor.getInstructions().getBlacklistedRepositoryLocationPatterns() |
| .add(new LocationPattern("mvn:" + selfGroupId + "/" + selfArtifactId)); |
| |
| for (String repository : uris) { |
| if (!processor.isRepositoryBlacklisted(repository)) { |
| downloader.download(repository, new DownloadCallback() { |
| @Override |
| public void downloaded(final StreamProvider provider) throws Exception { |
| try (InputStream is = provider.open()) { |
| Features featuresModel = JaxbUtil.unmarshal(provider.getUrl(), is, false); |
| processor.process(featuresModel); |
| synchronized (loaded) { |
| loaded.put(provider.getUrl(), featuresModel); |
| for (String innerRepository : featuresModel.getRepository()) { |
| if (!processor.isRepositoryBlacklisted(innerRepository)) { |
| downloader.download(innerRepository, this); |
| } |
| } |
| } |
| } |
| } |
| }); |
| } |
| } |
| downloader.await(); |
| return loaded; |
| } |
| |
| public static Set<String> getPrefixedProperties(Map<String, String> properties, String prefix) { |
| Set<String> result = new HashSet<>(); |
| for (String key : properties.keySet()) { |
| if (key.startsWith(prefix)) { |
| String url = properties.get(key); |
| if (url == null || url.length() == 0) { |
| url = key.substring(prefix.length()); |
| } |
| if (!url.isEmpty()) { |
| result.add(url); |
| } |
| } |
| } |
| return result; |
| } |
| |
| public static Map<String, Map<VersionRange, Map<String, String>>> getMetadata(Map<String, String> properties, String prefix) { |
| Map<String, Map<VersionRange, Map<String, String>>> result = new HashMap<>(); |
| for (String key : properties.keySet()) { |
| if (key.startsWith(prefix)) { |
| String val = properties.get(key); |
| key = key.substring(prefix.length()); |
| String[] parts = key.split("#"); |
| if (parts.length == 3) { |
| Map<VersionRange, Map<String, String>> ranges = |
| result.computeIfAbsent(parts[0], k -> new HashMap<>()); |
| String version = parts[1]; |
| if (!version.startsWith("[") && !version.startsWith("(")) { |
| Processor processor = new Processor(); |
| processor.setProperty("@", VersionTable.getVersion(version).toString()); |
| Macro macro = new Macro(processor); |
| version = macro.process("${range;[==,=+)}"); |
| } |
| VersionRange range = new VersionRange(version); |
| ranges.computeIfAbsent(range, k -> new HashMap<>()).put(parts[2], val); |
| } |
| } |
| } |
| return result; |
| } |
| |
| public static class FakeBundleRevision extends ResourceImpl implements BundleRevision, BundleStartLevel { |
| |
| private final Bundle bundle; |
| private int startLevel; |
| |
| public FakeBundleRevision(final Hashtable<String, String> headers, final String location, final long bundleId) throws BundleException { |
| ResourceBuilder.build(this, location, headers); |
| this.bundle = (Bundle) Proxy.newProxyInstance( |
| getClass().getClassLoader(), |
| new Class[] { Bundle.class }, |
| new InvocationHandler() { |
| @Override |
| public Object invoke(Object proxy, Method method, Object[] args) { |
| if (method.getName().equals("hashCode")) { |
| return FakeBundleRevision.this.hashCode(); |
| } else if (method.getName().equals("equals")) { |
| return proxy == args[0]; |
| } else if (method.getName().equals("toString")) { |
| return bundle.getSymbolicName() + "/" + bundle.getVersion(); |
| } else if (method.getName().equals("adapt")) { |
| if (args.length == 1 && args[0] == BundleRevision.class) { |
| return FakeBundleRevision.this; |
| } else if (args.length == 1 && args[0] == BundleStartLevel.class) { |
| return FakeBundleRevision.this; |
| } |
| } else if (method.getName().equals("getHeaders")) { |
| return headers; |
| } else if (method.getName().equals("getBundleId")) { |
| return bundleId; |
| } else if (method.getName().equals("getLocation")) { |
| return location; |
| } else if (method.getName().equals("getSymbolicName")) { |
| String name = headers.get(Constants.BUNDLE_SYMBOLICNAME); |
| int idx = name.indexOf(';'); |
| if (idx > 0) { |
| name = name.substring(0, idx).trim(); |
| } |
| return name; |
| } else if (method.getName().equals("getVersion")) { |
| return new Version(headers.get(Constants.BUNDLE_VERSION)); |
| } else if (method.getName().equals("getState")) { |
| return Bundle.ACTIVE; |
| } else if (method.getName().equals("getLastModified")) { |
| return 0l; |
| } |
| return null; |
| } |
| }); |
| } |
| |
| @Override |
| public int getStartLevel() { |
| return startLevel; |
| } |
| |
| @Override |
| public void setStartLevel(int startLevel) { |
| this.startLevel = startLevel; |
| } |
| |
| @Override |
| public boolean isPersistentlyStarted() { |
| return true; |
| } |
| |
| @Override |
| public boolean isActivationPolicyUsed() { |
| return false; |
| } |
| |
| @Override |
| public String getSymbolicName() { |
| return bundle.getSymbolicName(); |
| } |
| |
| @Override |
| public Version getVersion() { |
| return bundle.getVersion(); |
| } |
| |
| @Override |
| public List<BundleCapability> getDeclaredCapabilities(String namespace) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public List<BundleRequirement> getDeclaredRequirements(String namespace) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public int getTypes() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public BundleWiring getWiring() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Bundle getBundle() { |
| return bundle; |
| } |
| } |
| |
| public static class DummyDeployCallback extends StaticInstallSupport implements Deployer.DeployCallback { |
| |
| private final Bundle systemBundle; |
| private final Deployer.DeploymentState dstate; |
| private final AtomicLong nextBundleId = new AtomicLong(0); |
| |
| public DummyDeployCallback(Bundle sysBundle, Collection<Features> repositories) { |
| systemBundle = sysBundle; |
| dstate = new Deployer.DeploymentState(); |
| dstate.bundles = new HashMap<>(); |
| dstate.bundlesPerRegion = new HashMap<>(); |
| dstate.filtersPerRegion = new HashMap<>(); |
| dstate.state = new State(); |
| |
| MapUtils.addToMapSet(dstate.bundlesPerRegion, FeaturesService.ROOT_REGION, 0l); |
| dstate.bundles.put(0l, systemBundle); |
| |
| Collection<org.apache.karaf.features.Feature> features = new LinkedList<>(); |
| for (Features repo : repositories) { |
| if (repo.isBlacklisted()) { |
| continue; |
| } |
| for (Feature f : repo.getFeature()) { |
| if (!f.isBlacklisted()) { |
| features.add(f); |
| } |
| } |
| } |
| dstate.partitionFeatures(features); |
| } |
| |
| public Deployer.DeploymentState getDeploymentState() { |
| return dstate; |
| } |
| |
| @Override |
| public void saveState(State state) { |
| dstate.state.replace(state); |
| } |
| |
| @Override |
| public void persistResolveRequest(Deployer.DeploymentRequest request) { |
| } |
| |
| @Override |
| public void installConfigs(org.apache.karaf.features.Feature feature) { |
| } |
| |
| @Override |
| public void deleteConfigs(org.apache.karaf.features.Feature feature) throws IOException, InvalidSyntaxException { |
| } |
| |
| @Override |
| public void installLibraries(org.apache.karaf.features.Feature feature) { |
| } |
| |
| @Override |
| public void callListeners(FeatureEvent featureEvent) { |
| } |
| |
| @Override |
| public void callListeners(DeploymentEvent deployEvent) { |
| } |
| |
| @Override |
| public Bundle installBundle(String region, String uri, InputStream is) throws BundleException { |
| try { |
| Hashtable<String, String> headers = new Hashtable<>(); |
| ZipInputStream zis = new ZipInputStream(is); |
| ZipEntry entry; |
| while ((entry = zis.getNextEntry()) != null) { |
| if (MANIFEST_NAME.equals(entry.getName())) { |
| Attributes attributes = new Manifest(zis).getMainAttributes(); |
| for (Map.Entry<Object, Object> attr : attributes.entrySet()) { |
| headers.put(attr.getKey().toString(), attr.getValue().toString()); |
| } |
| } |
| } |
| BundleRevision revision = new FakeBundleRevision(headers, uri, nextBundleId.incrementAndGet()); |
| Bundle bundle = revision.getBundle(); |
| MapUtils.addToMapSet(dstate.bundlesPerRegion, region, bundle.getBundleId()); |
| dstate.bundles.put(bundle.getBundleId(), bundle); |
| return bundle; |
| } catch (IOException e) { |
| throw new BundleException("Unable to install bundle", e); |
| } |
| } |
| |
| @Override |
| public void bundleBlacklisted(BundleInfo bundleInfo) { |
| |
| } |
| |
| } |
| |
| public class MavenResolverLog extends org.apache.felix.resolver.Logger { |
| |
| public MavenResolverLog() { |
| super(Logger.LOG_DEBUG); |
| } |
| |
| @Override |
| protected void doLog(int level, String msg, Throwable throwable) { |
| switch (level) { |
| case LOG_DEBUG: |
| getLog().debug(msg, throwable); |
| break; |
| case LOG_INFO: |
| getLog().info(msg, throwable); |
| break; |
| case LOG_WARNING: |
| getLog().warn(msg, throwable); |
| break; |
| case LOG_ERROR: |
| getLog().error(msg, throwable); |
| break; |
| } |
| } |
| } |
| } |