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.buildcache;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.BufferedOutputStream;
26  import java.io.File;
27  import java.io.FileOutputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.nio.charset.StandardCharsets;
31  import java.nio.file.Path;
32  import java.nio.file.Paths;
33  import java.util.Enumeration;
34  import java.util.jar.Attributes;
35  import java.util.jar.JarEntry;
36  import java.util.jar.JarFile;
37  import java.util.jar.JarOutputStream;
38  
39  import org.apache.commons.io.FilenameUtils;
40  import org.apache.commons.io.IOUtils;
41  import org.apache.maven.buildcache.xml.CacheConfig;
42  import org.apache.maven.project.MavenProject;
43  import org.slf4j.Logger;
44  import org.slf4j.LoggerFactory;
45  
46  @Singleton
47  @Named
48  public class DefaultRestoredArtifactHandler implements RestoredArtifactHandler {
49  
50      private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRestoredArtifactHandler.class);
51      private static final String DIR_NAME = "cache-build-tmp";
52  
53      private final boolean adjustMetaInfVersion;
54  
55      @Inject
56      public DefaultRestoredArtifactHandler(CacheConfig cacheConfig) {
57          this.adjustMetaInfVersion = cacheConfig.adjustMetaInfVersion();
58      }
59  
60      @Override
61      public Path adjustArchiveArtifactVersion(MavenProject project, String originalArtifactVersion, Path artifactFile)
62              throws IOException {
63          if (!adjustMetaInfVersion) {
64              // option is disabled in cache configuration, return file as is
65              return artifactFile;
66          }
67  
68          File file = artifactFile.toFile();
69          if (project.getVersion().equals(originalArtifactVersion) || !CacheUtils.isArchive(file)) {
70              // versions of artifact and building project are the same or this is not an archive, return file as is
71              return artifactFile;
72          }
73  
74          File tempDirName = Paths.get(project.getBuild().getDirectory())
75                  .normalize()
76                  .resolve(DIR_NAME)
77                  .toFile();
78          if (tempDirName.mkdirs()) {
79              LOGGER.debug(
80                      "Temporary directory to restore artifact was created [artifactFile={}, "
81                              + "originalVersion={}, tempDir={}]",
82                      artifactFile,
83                      originalArtifactVersion,
84                      tempDirName);
85          }
86  
87          String currentVersion = project.getVersion();
88          File tmpJarFile = File.createTempFile(
89                  artifactFile.toFile().getName(), '.' + FilenameUtils.getExtension(file.getName()), tempDirName);
90          tmpJarFile.deleteOnExit();
91          String originalImplVersion = Attributes.Name.IMPLEMENTATION_VERSION + ": " + originalArtifactVersion;
92          String implVersion = Attributes.Name.IMPLEMENTATION_VERSION + ": " + currentVersion;
93          String commonXmlOriginalVersion = "<version>" + originalArtifactVersion + "</version>";
94          String commonXmlVersion = "<version>" + currentVersion + "</version>";
95          String originalPomPropsVersion = "version=" + originalArtifactVersion;
96          String pomPropsVersion = "version=" + currentVersion;
97          try (JarFile jarFile = new JarFile(artifactFile.toFile())) {
98              try (JarOutputStream jos =
99                      new JarOutputStream(new BufferedOutputStream(new FileOutputStream(tmpJarFile)))) {
100                 // Copy original jar file to the temporary one.
101                 Enumeration<JarEntry> jarEntries = jarFile.entries();
102                 byte[] buffer = new byte[1024];
103                 while (jarEntries.hasMoreElements()) {
104                     JarEntry entry = jarEntries.nextElement();
105                     String entryName = entry.getName();
106 
107                     if (entryName.startsWith("META-INF/maven")
108                             && (entryName.endsWith("plugin.xml") || entryName.endsWith("plugin-help.xml"))) {
109                         replaceEntry(jarFile, entry, commonXmlOriginalVersion, commonXmlVersion, jos);
110                         continue;
111                     }
112 
113                     if (entryName.endsWith("pom.xml")) {
114                         replaceEntry(jarFile, entry, commonXmlOriginalVersion, commonXmlVersion, jos);
115                         continue;
116                     }
117 
118                     if (entryName.endsWith("pom.properties")) {
119                         replaceEntry(jarFile, entry, originalPomPropsVersion, pomPropsVersion, jos);
120                         continue;
121                     }
122 
123                     if (JarFile.MANIFEST_NAME.equals(entryName)) {
124                         replaceEntry(jarFile, entry, originalImplVersion, implVersion, jos);
125                         continue;
126                     }
127                     jos.putNextEntry(entry);
128                     try (InputStream entryInputStream = jarFile.getInputStream(entry)) {
129                         int bytesRead;
130                         while ((bytesRead = entryInputStream.read(buffer)) != -1) {
131                             jos.write(buffer, 0, bytesRead);
132                         }
133                     }
134                 }
135             }
136         }
137         return tmpJarFile.toPath();
138     }
139 
140     private static void replaceEntry(
141             JarFile jarFile, JarEntry entry, String toReplace, String replacement, JarOutputStream jos)
142             throws IOException {
143         String fullManifest = IOUtils.toString(jarFile.getInputStream(entry), StandardCharsets.UTF_8.name());
144         String modified = fullManifest.replaceAll(toReplace, replacement);
145 
146         byte[] bytes = modified.getBytes(StandardCharsets.UTF_8);
147         JarEntry newEntry = new JarEntry(entry.getName());
148         jos.putNextEntry(newEntry);
149         jos.write(bytes);
150     }
151 }