Skip to content

Commit

Permalink
[ARQ-1931] Fixes NPE when directory named after Java archive
Browse files Browse the repository at this point in the history
When the directory has a suffix of `[.jar, .war, .ear, .rar]` NPE is thrown when recursively processing subarchives, as the directory matches the pattern,  but has no content (`null` asset).
  • Loading branch information
dipak-pawar authored and bartoszmajsak committed Dec 5, 2016
1 parent d199e93 commit e27fa74
Show file tree
Hide file tree
Showing 17 changed files with 602 additions and 467 deletions.
524 changes: 268 additions & 256 deletions pom.xml

Large diffs are not rendered by default.

@@ -0,0 +1,58 @@
package org.jboss.arquillian.extension.jacoco.client;

import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.Filter;
import org.jboss.shrinkwrap.api.Filters;
import org.jboss.shrinkwrap.api.Node;
import org.jboss.shrinkwrap.api.asset.Asset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;

import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ArchiveInstrumenter
{

private static final Logger LOGGER = Logger.getLogger(JaCoCoApplicationArchiveProcessor.class.getName());

private final SignatureRemover signatureRemover;

public ArchiveInstrumenter(SignatureRemover signatureRemover)
{
this.signatureRemover = signatureRemover;
}

public void processArchive(Archive<?> archive, Filter<ArchivePath> filter)
{
instrument(archive, archive.getContent(filter));
signatureRemover.removeSignatures(archive);

final Map<ArchivePath, Node> jars = archive.getContent(Filters.include(".*\\.(jar|war|rar|ear)$"));
for (Map.Entry<ArchivePath, Node> entry : jars.entrySet())
{
// Should have used genericArchive, but with GenericArchive we need
// to specify a ArchiveFormat and that trigger this SHRINKWRAP-474
final JavaArchive subArchive = archive.getAsType(JavaArchive.class, entry.getKey());
if (subArchive == null) // [ARQ-1931] Asset is null which means we are dealing with the directory
{
LOGGER.log(Level.WARNING, String.format("directory path %s ends one of the suffixes: [.ear, .war, .rar, .jar]", entry.getValue()));
}
else
{
processArchive(subArchive, filter);
}
}
}

private void instrument(Archive<?> archive, Map<ArchivePath, Node> classes)
{
for (Map.Entry<ArchivePath, Node> entry : classes.entrySet())
{
final Asset original = entry.getValue().getAsset();
archive.delete(entry.getKey());
archive.add(new InstrumentedAsset(original), entry.getKey());
}
}
}
@@ -1,12 +1,5 @@
package org.jboss.arquillian.extension.jacoco.client;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.UUID;

import org.jacoco.core.data.ExecutionData;
import org.jacoco.core.data.ExecutionDataReader;
import org.jacoco.core.data.ExecutionDataStore;
Expand All @@ -16,6 +9,13 @@
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.extension.jacoco.CoverageDataCommand;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.UUID;

public class CoverageDataReceiver
{
public void storeCoverageData(@Observes CoverageDataCommand coverageDataCommandEvent)
Expand Down Expand Up @@ -43,7 +43,7 @@ private void copyToAgentExecutionStore(ExecutionDataStore dataStore) throws Exce
Field f = UUID.class.getDeclaredField("$jacocoAccess");
Object executor = f.get(null);

Method m = executor.getClass().getDeclaredMethod("getProbes", new Class[] {Object[].class});
Method m = executor.getClass().getDeclaredMethod("getProbes", Object[].class);
m.setAccessible(true);
for (ExecutionData data : dataStore.getContents())
{
Expand All @@ -62,7 +62,6 @@ private void copyToAgentExecutionStore(ExecutionDataStore dataStore) throws Exce


private void read(InputStream stream, IExecutionDataVisitor executionDataVisitor, ISessionInfoVisitor sessionVisitor)
throws IOException
{
ExecutionDataReader reader = new ExecutionDataReader(stream);

Expand Down
Expand Up @@ -16,14 +16,14 @@
*/
package org.jboss.arquillian.extension.jacoco.client;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

import org.jacoco.core.instr.Instrumenter;
import org.jacoco.core.runtime.IRuntime;
import org.jboss.arquillian.extension.jacoco.container.ArquillianRuntime;
import org.jboss.shrinkwrap.api.asset.Asset;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

/**
* Instrument the underlying Class using the Jacoco Runtime.
*
Expand All @@ -32,13 +32,13 @@
* @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a>
* @version $Revision: $
*/
public class InstrumenterAsset implements Asset
public class InstrumentedAsset implements Asset
{
private Asset asset;
private final Asset asset;

public static final String EX_STRING = "arquillian";

public InstrumenterAsset(Asset asset)
public InstrumentedAsset(Asset asset)
{
this.asset = asset;
}
Expand All @@ -50,10 +50,9 @@ public InputStream openStream()
{
try
{
IRuntime runtime = ArquillianRuntime.getInstance();
Instrumenter instrumenter = new Instrumenter(runtime);
byte[] instrumented = instrumenter.instrument(asset.openStream(), EX_STRING);

final IRuntime runtime = ArquillianRuntime.getInstance();
final Instrumenter instrumenter = new Instrumenter(runtime);
final byte[] instrumented = instrumenter.instrument(asset.openStream(), EX_STRING);
return new ByteArrayInputStream(instrumented);
}
catch (Exception e)
Expand Down
Expand Up @@ -16,63 +16,29 @@
*/
package org.jboss.arquillian.extension.jacoco.client;

import java.util.Map;
import java.util.Map.Entry;

import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.test.spi.TestClass;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.Filter;
import org.jboss.shrinkwrap.api.Filters;
import org.jboss.shrinkwrap.api.Node;
import org.jboss.shrinkwrap.api.asset.Asset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;

/**
* Instrument all Classes (or their subset if found in the User defined
*
* @Deployment.
*
*
* @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a>
* @author <a href="mailto:lkrejci@redhat.com">Lukas Krejci</a>
*
* @version $Revision: $
* @Deployment.
*/
public class ApplicationArchiveInstrumenter implements
ApplicationArchiveProcessor
public class JaCoCoApplicationArchiveProcessor implements ApplicationArchiveProcessor
{

@Inject
private Instance<JacocoConfiguration> config;

private void processArchive(Archive<?> archive, Filter<ArchivePath> filter)
{
SignatureRemover signatureRemover = new SignatureRemover();

Map<ArchivePath, Node> classes = archive.getContent(filter);

for (Entry<ArchivePath, Node> entry : classes.entrySet())
{
Asset original = entry.getValue().getAsset();
archive.delete(entry.getKey());
archive.add(new InstrumenterAsset(original), entry.getKey());
}
signatureRemover.removeSignatures(archive);
// Process sub-archives recursively
Map<ArchivePath, Node> jars = archive.getContent(Filters.include(".*\\.(jar|war|rar|ear)$"));
for (Entry<ArchivePath, Node> entry : jars.entrySet())
{
// Should have used genericArchive, but with GenericArchive we need
// to specify a ArchiveFormat and that trigger this SHRINKWRAP-474
JavaArchive subArchive = archive.getAsType(JavaArchive.class, entry.getKey());
processArchive(subArchive, filter);
}
}

public void process(Archive<?> applicationArchive, TestClass testClass)
{
processArchive(applicationArchive, config.get().getClassFilter());
new ArchiveInstrumenter(new SignatureRemover()).processArchive(applicationArchive, config.get().getClassFilter());
}

}
Expand Up @@ -18,6 +18,10 @@

package org.jboss.arquillian.extension.jacoco.client;

import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.Filter;
import org.jboss.shrinkwrap.api.Filters;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -26,23 +30,21 @@
import java.util.Map;
import java.util.UUID;

import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.Filter;
import org.jboss.shrinkwrap.api.Filters;

/**
* @author Lukas Krejci
*/
public class JacocoConfiguration
{

public static final Filter<ArchivePath> ALL_CLASSES = Filters.include(".*\\.class");

private static final String INCLUDES_PROPERTY = "includes";
public static final String INCLUDES_DEFAULT_VALUE = null;
private static final String EXCLUDES_PROPERTY = "excludes";
public static final String EXCLUDES_DEFAULT_VALUE = null;
private static final String APPEND_ASM_LIBRARY_PROPERTY = "appendAsmLibrary";
private static final String APPEND_ASM_LIBRARY_DEFAULT = "true";

private static final String APPEND_ASM_LIBRARY_DEFAULT = "true";
private static final String SEPARATOR = "\\s*;\\s*";

private List<String> includes;
Expand Down Expand Up @@ -98,11 +100,11 @@ public static JacocoConfiguration fromMap(Map<String, String> map)
ConfigMap c = new ConfigMap(map);

String incls = c.get(INCLUDES_PROPERTY, INCLUDES_DEFAULT_VALUE);
ret.includes = incls == null ? Collections.<String> emptyList() : Arrays
ret.includes = incls == null ? Collections.<String>emptyList() : Arrays
.asList(incls.split(SEPARATOR));

String excls = c.get(EXCLUDES_PROPERTY, EXCLUDES_DEFAULT_VALUE);
ret.excludes = excls == null ? Collections.<String> emptyList() : Arrays
ret.excludes = excls == null ? Collections.<String>emptyList() : Arrays
.asList(excls.split(SEPARATOR));

ret.composedFilter = ret.composeFilter();
Expand Down Expand Up @@ -130,13 +132,13 @@ public Filter<ArchivePath> getClassFilter()

public boolean isAppendAsmLibrary()
{
return appendAsmLibrary;
return appendAsmLibrary;
}

private Filter<ArchivePath> composeFilter()
{
List<Filter<ArchivePath>> filters = new ArrayList<Filter<ArchivePath>>();
filters.add(Filters.include(".*\\.class"));
filters.add(ALL_CLASSES);

for (String include : getIncludeRegexps())
{
Expand Down Expand Up @@ -166,9 +168,10 @@ private List<String> convertToRegexps(List<String> patterns)
if (patterns.isEmpty())
{
return patterns;
} else
}
else
{
ArrayList<String> ret = new ArrayList<String>(patterns.size());
final List<String> ret = new ArrayList<String>(patterns.size());
for (String regexp : patterns)
{
regexp = regexp.replace(".", "\\/").replace("*", ".*")
Expand All @@ -186,8 +189,7 @@ public static boolean isJacocoAgentActive()
try
{
UUID.class.getDeclaredField("$jacocoAccess");
}
catch (Exception e)
} catch (Exception e)
{
return false;
}
Expand Down
Expand Up @@ -35,7 +35,7 @@ public void register(ExtensionBuilder builder)
if(JacocoConfiguration.isJacocoAgentActive())
{
builder.service(AuxiliaryArchiveAppender.class, JacocoArchiveAppender.class)
.service(ApplicationArchiveProcessor.class, ApplicationArchiveInstrumenter.class);
.service(ApplicationArchiveProcessor.class, JaCoCoApplicationArchiveProcessor.class);

builder.observer(CoverageDataReceiver.class)
.observer(JacocoConfigurator.class);
Expand Down
Expand Up @@ -15,7 +15,7 @@
import org.jboss.shrinkwrap.api.asset.Asset;

/**
* Signed jars must have their signatures removed. Since {@link InstrumenterAsset} only deals with classes we must actually
* Signed jars must have their signatures removed. Since {@link InstrumentedAsset} only deals with classes we must actually
* perform the duties of {@link org.jacoco.core.internal.instr.SignatureRemover} ourselves
*
* @see org.jacoco.core.internal.instr.SignatureRemover
Expand Down

0 comments on commit e27fa74

Please sign in to comment.