blob: 2d5de7abf175073acb660311c2d04ab1cac900eb [file] [log] [blame]
/*
* 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.hadoop.maven.plugin.shade.resource;
import java.io.BufferedReader;
import org.apache.maven.plugins.shade.relocation.Relocator;
import org.apache.maven.plugins.shade.resource.ResourceTransformer;
import org.codehaus.plexus.util.IOUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
/**
* Resources transformer that appends entries in META-INF/services resources
* into a single resource. For example, if there are several
* META-INF/services/org.apache.maven.project.ProjectBuilder resources spread
* across many JARs the individual entries will all be concatenated into a
* single META-INF/services/org.apache.maven.project.ProjectBuilder resource
* packaged into the resultant JAR produced by the shading process.
*
* From following sources, only needed until MSHADE-182 gets released
* * https://s.apache.org/vwjl (source in maven-shade-plugin repo)
* * https://issues.apache.org/jira/secure/attachment/12718938/MSHADE-182.patch
*
* Has been reformatted according to Hadoop checkstyle rules and modified
* to meet Hadoop's threshold for Findbugs problems.
*/
public class ServicesResourceTransformer
implements ResourceTransformer {
private static final String SERVICES_PATH = "META-INF/services";
private Map<String, ServiceStream> serviceEntries = new HashMap<>();
private List<Relocator> relocators;
public boolean canTransformResource(String resource) {
if (resource.startsWith(SERVICES_PATH)) {
return true;
}
return false;
}
public void processResource(String resource, InputStream is,
List<Relocator> relocatorz) throws IOException {
ServiceStream out = serviceEntries.get(resource);
if (out == null) {
out = new ServiceStream();
serviceEntries.put(resource, out);
}
out.append(is);
is.close();
if (this.relocators == null) {
this.relocators = relocatorz;
}
}
public boolean hasTransformedResource() {
return serviceEntries.size() > 0;
}
public void modifyOutputStream(JarOutputStream jos)
throws IOException {
for (Map.Entry<String, ServiceStream> entry : serviceEntries.entrySet()) {
String key = entry.getKey();
ServiceStream data = entry.getValue();
if (relocators != null) {
key = key.substring(SERVICES_PATH.length() + 1);
for (Relocator relocator : relocators) {
if (relocator.canRelocateClass(key)) {
key = relocator.relocateClass(key);
break;
}
}
key = SERVICES_PATH + '/' + key;
}
jos.putNextEntry(new JarEntry(key));
//read the content of service file for candidate classes for relocation
//presume everything is UTF8, because Findbugs barfs on default
//charset and this seems no worse a choice ¯\_(ツ)_/¯
PrintWriter writer = new PrintWriter(new OutputStreamWriter(jos,
StandardCharsets.UTF_8));
InputStreamReader streamReader =
new InputStreamReader(data.toInputStream(), StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader);
String className;
while ((className = reader.readLine()) != null) {
if (relocators != null) {
for (Relocator relocator : relocators) {
//if the class can be relocated then relocate it
if (relocator.canRelocateClass(className)) {
className = relocator.applyToSourceContent(className);
break;
}
}
}
writer.println(className);
writer.flush();
}
reader.close();
data.reset();
}
}
static class ServiceStream extends ByteArrayOutputStream {
public ServiceStream() {
super(1024);
}
public void append(InputStream is)
throws IOException {
if (count > 0 && buf[count - 1] != '\n' && buf[count - 1] != '\r') {
write('\n');
}
IOUtil.copy(is, this);
}
public InputStream toInputStream() {
return new ByteArrayInputStream(buf, 0, count);
}
}
}