View Javadoc
1   /*
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
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
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
17   * under the License.
18   */
19  package org.apache.maven.repository.internal;
20  
21  import java.io.Reader;
22  import java.nio.file.Files;
23  import java.nio.file.Path;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.Date;
27  import java.util.Iterator;
28  import java.util.LinkedHashMap;
29  import java.util.Map;
30  import java.util.jar.JarFile;
31  import java.util.zip.ZipEntry;
32  
33  import org.apache.maven.repository.internal.PluginsMetadata.PluginInfo;
34  import org.codehaus.plexus.util.ReaderFactory;
35  import org.codehaus.plexus.util.xml.Xpp3Dom;
36  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
37  import org.eclipse.aether.RepositorySystemSession;
38  import org.eclipse.aether.artifact.Artifact;
39  import org.eclipse.aether.deployment.DeployRequest;
40  import org.eclipse.aether.impl.MetadataGenerator;
41  import org.eclipse.aether.installation.InstallRequest;
42  import org.eclipse.aether.metadata.Metadata;
43  import org.eclipse.aether.util.ConfigUtils;
44  
45  /**
46   * Maven G level metadata generator.
47   * <p>
48   * Plugin metadata contains G level list of "prefix" to A mapping for plugins present under this G.
49   */
50  class PluginsMetadataGenerator implements MetadataGenerator {
51      private static final String PLUGIN_DESCRIPTOR_LOCATION = "META-INF/maven/plugin.xml";
52  
53      private final Map<Object, PluginsMetadata> processedPlugins;
54  
55      private final Date timestamp;
56  
57      PluginsMetadataGenerator(RepositorySystemSession session, InstallRequest request) {
58          this(session, request.getMetadata());
59      }
60  
61      PluginsMetadataGenerator(RepositorySystemSession session, DeployRequest request) {
62          this(session, request.getMetadata());
63      }
64  
65      private PluginsMetadataGenerator(RepositorySystemSession session, Collection<? extends Metadata> metadatas) {
66          this.processedPlugins = new LinkedHashMap<>();
67          this.timestamp = (Date) ConfigUtils.getObject(session, new Date(), "maven.startTime");
68  
69          /*
70           * NOTE: This should be considered a quirk to support interop with Maven's legacy ArtifactDeployer which
71           * processes one artifact at a time and hence cannot associate the artifacts from the same project to use the
72           * same version index. Allowing the caller to pass in metadata from a previous deployment allows to re-establish
73           * the association between the artifacts of the same project.
74           */
75          for (Iterator<? extends Metadata> it = metadatas.iterator(); it.hasNext(); ) {
76              Metadata metadata = it.next();
77              if (metadata instanceof PluginsMetadata) {
78                  it.remove();
79                  PluginsMetadata pluginMetadata = (PluginsMetadata) metadata;
80                  processedPlugins.put(pluginMetadata.getGroupId(), pluginMetadata);
81              }
82          }
83      }
84  
85      @Override
86      public Collection<? extends Metadata> prepare(Collection<? extends Artifact> artifacts) {
87          return Collections.emptyList();
88      }
89  
90      @Override
91      public Artifact transformArtifact(Artifact artifact) {
92          return artifact;
93      }
94  
95      @Override
96      public Collection<? extends Metadata> finish(Collection<? extends Artifact> artifacts) {
97          LinkedHashMap<String, PluginsMetadata> plugins = new LinkedHashMap<>();
98          for (Artifact artifact : artifacts) {
99              PluginInfo pluginInfo = extractPluginInfo(artifact);
100             if (pluginInfo != null) {
101                 String key = pluginInfo.groupId;
102                 if (processedPlugins.get(key) == null) {
103                     PluginsMetadata pluginMetadata = plugins.get(key);
104                     if (pluginMetadata == null) {
105                         pluginMetadata = new PluginsMetadata(pluginInfo, timestamp);
106                         plugins.put(key, pluginMetadata);
107                     }
108                 }
109             }
110         }
111         return plugins.values();
112     }
113 
114     private PluginInfo extractPluginInfo(Artifact artifact) {
115         // sanity: jar, no classifier and file exists
116         if (artifact != null
117                 && "jar".equals(artifact.getExtension())
118                 && "".equals(artifact.getClassifier())
119                 && artifact.getFile() != null) {
120             Path artifactPath = artifact.getFile().toPath();
121             if (Files.isRegularFile(artifactPath)) {
122                 try (JarFile artifactJar = new JarFile(artifactPath.toFile(), false)) {
123                     ZipEntry pluginDescriptorEntry = artifactJar.getEntry(PLUGIN_DESCRIPTOR_LOCATION);
124 
125                     if (pluginDescriptorEntry != null) {
126                         try (Reader reader =
127                                 ReaderFactory.newXmlReader(artifactJar.getInputStream(pluginDescriptorEntry))) {
128                             // Note: using DOM instead of use of
129                             // org.apache.maven.plugin.descriptor.PluginDescriptor
130                             // as it would pull in dependency on:
131                             // - maven-plugin-api (for model)
132                             // - Plexus Container (for model supporting classes and exceptions)
133                             Xpp3Dom root = Xpp3DomBuilder.build(reader);
134                             String groupId = root.getChild("groupId").getValue();
135                             String artifactId = root.getChild("artifactId").getValue();
136                             String goalPrefix = root.getChild("goalPrefix").getValue();
137                             String name = root.getChild("name").getValue();
138                             return new PluginInfo(groupId, artifactId, goalPrefix, name);
139                         }
140                     }
141                 } catch (Exception e) {
142                     // here we can have: IO. ZIP or Plexus Conf Ex: but we should not interfere with user intent
143                 }
144             }
145         }
146         return null;
147     }
148 }