Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions log4j-transform-maven-shade-plugin-extensions/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-transform-parent</artifactId>
<version>${revision}</version>
<relativePath>../log4j-transform-parent</relativePath>
</parent>
<artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId>
<packaging>jar</packaging>
<name>Apache Log4j Maven Shade Plugin Transformer</name>
<description>Transformer implementation to concatenate Log4j2Plugins.dat files</description>
<properties>
<docLabel>Shaded Plugin Log4j2 Transformer</docLabel>
<maven.doap.skip>true</maven.doap.skip>
</properties>

<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>

<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<outputDirectory>${project.build.directory}/jacoco-report</outputDirectory>
<rules>
<rule>
<element>PACKAGE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.96</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
<executions>
<execution>
<id>prepare-code-coverage</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report-code-coverage</id>
<goals>
<goal>report</goal>
</goals>
<configuration>
<outputDirectory>${project.reporting.outputDirectory}/jacoco-aggregate</outputDirectory>
</configuration>
</execution>
<execution>
<id>verify-test-coverage</id>
<goals>
<goal>check</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.logging.log4j.maven.plugins.shade.transformer;


import java.io.IOException;
import java.io.OutputStream;

import org.apache.commons.io.output.ProxyOutputStream;

import static org.apache.commons.io.output.ClosedOutputStream.CLOSED_OUTPUT_STREAM;

/**
* Suppress the close of underlying output stream.
*/
final class CloseShieldOutputStream extends ProxyOutputStream {

/**
* @param out the OutputStream to delegate to
*/
/* default */ CloseShieldOutputStream(final OutputStream out) {
super(out);
}


@Override
public void close() throws IOException {
out.flush();
out = CLOSED_OUTPUT_STREAM;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
* 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.logging.log4j.maven.plugins.shade.transformer;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;

import org.apache.logging.log4j.core.config.plugins.processor.PluginCache;
import org.apache.logging.log4j.core.config.plugins.processor.PluginEntry;

import org.apache.maven.plugins.shade.relocation.Relocator;
import org.apache.maven.plugins.shade.resource.ReproducibleResourceTransformer;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

import static org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE;

/**
* 'log4j-maven-shade-plugin' transformer implementation.
*/
public class Log4j2PluginCacheFileTransformer
implements ReproducibleResourceTransformer {

/**
* Log4j config files to share across the transformation stages.
*/
private final List<Path> tempFiles;
/**
* {@link Relocator} instances to share across the transformation stages.
*/
private final List<Relocator> tempRelocators;
/**
* Store youngest (i.e. largest millisecond) so that we can produce reproducible jar file
*/
private long youngestTime = 0;


/**
* Default constructor, initializing internal state.
*/
public Log4j2PluginCacheFileTransformer() {
tempRelocators = new ArrayList<>();
tempFiles = new ArrayList<>();
}

/**
* @param resource resource to check
* @return true when resource is recognized as log4j-plugin-cache file
*/
@Override
public boolean canTransformResource(final String resource) {
return PLUGIN_CACHE_FILE.equals(resource);
}

@Override
@Deprecated
public void processResource(String resource, InputStream is, List<Relocator> relocators) {
// stub
}

/**
* @param resource ignored parameter
* @param resourceInput resource input stream to save in temp file
* for next stage
* @param relocators relocators to keep for next stage
* @throws IOException thrown by file writing errors
*/
@Override
public void processResource(final String resource,
final InputStream resourceInput,
final List<Relocator> relocators,
final long time) throws IOException {
final Path tempFile = Files.createTempFile("Log4j2Plugins", "dat");
Files.copy(resourceInput, tempFile, REPLACE_EXISTING);
tempFiles.add(tempFile);
youngestTime = Math.max(youngestTime, time);

if (relocators != null) {
this.tempRelocators.addAll(relocators);
}
}

/**
* @return true when several log4j-cache-files should be merged
* or at least one relocated.
*/
@Override
public boolean hasTransformedResource() {
return tempFiles.size() > 1
|| !tempFiles.isEmpty() && !tempRelocators.isEmpty();
}


/**
* Stores all previously collected log4j-cache-files to the target jar.
*
* @param jos jar output
* @throws IOException When the IO blows up
*/
@Override
public void modifyOutputStream(final JarOutputStream jos)
throws IOException {
try {
final PluginCache aggregator = new PluginCache();
aggregator.loadCacheFiles(getUrls());
relocatePlugin(tempRelocators, aggregator.getAllCategories());
putJarEntry(jos);
// prevent the aggregator to close the jar output
final CloseShieldOutputStream outputStream =
new CloseShieldOutputStream(jos);
aggregator.writeCache(outputStream);
} finally {
deleteTempFiles();
}
}

private Enumeration<URL> getUrls() throws MalformedURLException {
final List<URL> urls = new ArrayList<>();
for (final Path tempFile : tempFiles) {
final URL url = tempFile.toUri().toURL();
urls.add(url);
}
return Collections.enumeration(urls);
}

/**
* Applies the given {@code relocators} to the {@code aggregator}.
*
* @param relocators relocators.
* @param aggregatorCategories all categories of the aggregator
*/
/* default */ void relocatePlugin(final List<Relocator> relocators,
Map<String, Map<String, PluginEntry>> aggregatorCategories) {
for (final Entry<String, Map<String, PluginEntry>> categoryEntry
: aggregatorCategories.entrySet()) {
for (final Entry<String, PluginEntry> pluginMapEntry
: categoryEntry.getValue().entrySet()) {
final PluginEntry pluginEntry = pluginMapEntry.getValue();
final String originalClassName = pluginEntry.getClassName();

final Relocator matchingRelocator = findFirstMatchingRelocator(
originalClassName, relocators);

if (matchingRelocator != null) {
final String newClassName = matchingRelocator
.relocateClass(originalClassName);
pluginEntry.setClassName(newClassName);
}
}
}
}

private Relocator findFirstMatchingRelocator(final String originalClassName,
final List<Relocator> relocators) {
Relocator result = null;
for (final Relocator relocator : relocators) {
if (relocator.canRelocateClass(originalClassName)) {
result = relocator;
break;
}
}
return result;
}

private void putJarEntry(JarOutputStream jos) throws IOException {
final JarEntry jarEntry = new JarEntry(PLUGIN_CACHE_FILE);

// Set time to youngest timestamp, to ensure reproducible output.
final FileTime fileTime = FileTime.fromMillis(youngestTime);
jarEntry.setLastModifiedTime(fileTime);

jos.putNextEntry(jarEntry);
}

private void deleteTempFiles() throws IOException {
final ListIterator<Path> pathIterator = tempFiles.listIterator();
while (pathIterator.hasNext()) {
final Path path = pathIterator.next();
Files.deleteIfExists(path);
pathIterator.remove();
}
}
}
Loading