Add .tox/ to .gitignore
[odlparent.git] / karaf / karaf-maven-plugin / src / main / java / org / apache / karaf / tooling / features / ExportFeatureMetaDataMojo.java
1 /**
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one or more
4  * contributor license agreements.  See the NOTICE file distributed with
5  * this work for additional information regarding copyright ownership.
6  * The ASF licenses this file to You under the Apache License, Version 2.0
7  * (the "License"); you may not use this file except in compliance with
8  * the License.  You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.karaf.tooling.features;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStream;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.jar.Attributes;
30 import java.util.jar.JarInputStream;
31 import java.util.jar.Manifest;
32
33 import org.apache.karaf.features.internal.model.Bundle;
34 import org.apache.karaf.features.internal.model.Feature;
35 import org.apache.karaf.features.internal.model.Features;
36 import org.apache.karaf.features.internal.model.JaxbUtil;
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;
44 import org.osgi.framework.Version;
45
46 /**
47  * Export meta data about features
48  */
49 @Mojo(name = "features-export-meta-data", defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
50 public class ExportFeatureMetaDataMojo extends AbstractFeatureMojo {
51     
52     /**
53      * If set to true then all bundles will be merged into one combined feature.
54      * In this case duplicates will be eliminated
55      */
56     @Parameter
57     private boolean mergedFeature;
58     
59     /**
60      * If set to true then for each bundle symbolic name only the highest version will be used
61      */
62     @Parameter
63     protected boolean oneVersion;
64
65     /**
66      * Name of the file for exported feature meta data
67      */
68     @Parameter(defaultValue = "${project.build.directory}/features.xml")
69     private File metaDataFile;
70     
71     public void execute() throws MojoExecutionException, MojoFailureException {
72         Set<Feature> featuresSet = resolveFeatures();
73         if (mergedFeature) {
74             Feature feature = oneVersion ? mergeFeatureOneVersion(featuresSet) : mergeFeature(featuresSet);
75             featuresSet = new HashSet<Feature>();
76             featuresSet.add(feature);
77         }
78         try {
79             metaDataFile.getParentFile().mkdirs();
80             Features features = new Features();
81             features.getFeature().addAll(featuresSet);
82             try (OutputStream os = new FileOutputStream(metaDataFile)) {
83                 JaxbUtil.marshal(features, os);
84             }
85         } catch (Exception e) {
86             throw new RuntimeException("Error writing feature meta data to " + metaDataFile + ": " + e.getMessage(), e);
87         }
88     }
89
90     private Feature mergeFeature(Set<Feature> featuresSet) throws MojoExecutionException {
91         Feature merged = new Feature("merged");
92         Set<String> bundleIds = new HashSet<String>();
93         for (Feature feature : featuresSet) {
94             for (Bundle bundle : feature.getBundle()) {
95                 String symbolicName = getBundleSymbolicName(bundle);
96                 if (symbolicName == null) {
97                     logIgnored(bundle);
98                     continue;
99                 }
100                 String bundleId = symbolicName + ":" + getBundleVersion(bundle);
101                 if (!bundleIds.contains(bundleId)) {
102                     bundleIds.add(bundleId);
103                     merged.getBundle().add(bundle);
104                 }
105             }
106         }
107         return merged;
108     }
109     
110     private Feature mergeFeatureOneVersion(Set<Feature> featuresSet) throws MojoExecutionException {
111         Feature merged = new Feature("merged");
112         Map<String, Bundle> bundleVersions = new HashMap<>();
113         for (Feature feature : featuresSet) {
114             for (Bundle bundle : feature.getBundle()) {
115                 String symbolicName = getBundleSymbolicName(bundle);
116                 if (symbolicName == null) {
117                     logIgnored(bundle);
118                     continue;
119                 }
120                 Bundle existingBundle = bundleVersions.get(symbolicName);
121                 if (existingBundle != null) {
122                     Version existingVersion = new Version(getBundleVersion(existingBundle));
123                     Version newVersion = new Version(getBundleVersion(bundle));
124                     if (newVersion.compareTo(existingVersion) > 0) {
125                         bundleVersions.put(symbolicName, bundle);
126                     }
127                 } else {
128                     bundleVersions.put(symbolicName, bundle);
129                 }
130             }
131         }
132         for (Bundle bundle : bundleVersions.values()) {
133             merged.getBundle().add(bundle);
134         }
135         return merged;
136     }
137
138     private void logIgnored(Bundle bundle) {
139         getLog().warn("Ignoring jar without BundleSymbolicName: " + bundle.getLocation());
140     }
141
142     private Map<String, Attributes> manifests = new HashMap<>();
143
144     private String getBundleVersion(Bundle bundle) throws MojoExecutionException {
145         return getManifest(bundle).getValue("Bundle-Version");
146     }
147
148     private String getBundleSymbolicName(Bundle bundle) throws MojoExecutionException {
149         return getManifest(bundle).getValue("Bundle-SymbolicName");
150     }
151
152     private Attributes getManifest(Bundle bundle) throws MojoExecutionException {
153         Attributes attributes = manifests.get(bundle.getLocation());
154         if (attributes == null) {
155             Artifact artifact = resourceToArtifact(bundle.getLocation(), skipNonMavenProtocols);
156             if (artifact.getFile() == null) {
157                 resolveArtifact(artifact, remoteRepos);
158             }
159             try (JarInputStream jis = new JarInputStream(new FileInputStream(artifact.getFile()))) {
160                 Manifest manifest = jis.getManifest();
161                 if (manifest != null) {
162                     attributes = manifest.getMainAttributes();
163                 } else {
164                     attributes = new Attributes();
165                 }
166                 manifests.put(bundle.getLocation(), attributes);
167             } catch (IOException e) {
168                 throw new MojoExecutionException("Error reading bundle manifest from " + bundle.getLocation(), e);
169             }
170         }
171         return attributes;
172     }
173
174 }