Deiwin
Deiwin

Reputation: 447

Accessing compiled resources in a Pax Exam contained test

I've been trying to run a JMH benchmark test in an OSGI container created with Pax Exam (similarly to how it's described in this SO answer, but with Pax Exam thrown into the mix as well). But am having some problems getting the JMH generated resource files created during the compilation to load.

After the tests have been compiled, the following can be found in the target/test-classes directory:

$ ls -l target/test-classes/META-INF
BenchmarkList  CompilerHints

In a test I use some code (that I cannot change) that (effectively) looks for the file like so:

getClass().getClassLoader().getResources("META-INF/BenchmarkList");

Running this fails (returns 0 results) and I get the following error:

java.lang.RuntimeException: ERROR: Unable to find the resource: /META-INF/BenchmarkList
at org.openjdk.jmh.runner.AbstractResourceReader.getReaders(AbstractResourceReader.java:96)
at org.openjdk.jmh.runner.BenchmarkList.find(BenchmarkList.java:104)
at org.openjdk.jmh.runner.Runner.internalRun(Runner.java:228)
at org.openjdk.jmh.runner.Runner.run(Runner.java:178)
at com.company.project.performance.MyBenchmarkTest.launchBenchmark(MyBenchmarkTest.java:145)

I've tried creating a bundle containing the file, like so:

streamBundle(bundle()
  .add("META-INF/BenchmarkList", new FileInputStream("target/test-classes/META-INF/BenchmarkList"))
  .build()),

Which does create a JAR with the following contents:

META-INF/MANIFEST.MF
META-INF/
META-INF/BenchmarkList

But the problem persists. How could I make the resource file available for JMH?

An MCVE:

pom.xml:

<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>

    <groupId>com.company.project</groupId>
    <artifactId>performance</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>

    <name>MCVE for JMH+PaxExam issue</name>


    <prerequisites>
        <maven>3.0</maven>
    </prerequisites>

    <properties>
        <apache-servicemix.version>5.4.0</apache-servicemix.version>
        <junit.version>4.11</junit.version>
        <jmh.version>1.10.1</jmh.version>
        <pax-exam.version>4.4.0</pax-exam.version>
        <tinybundles.version>2.1.0</tinybundles.version>

        <maven-sunfire-report-plugin.version>2.18.1</maven-sunfire-report-plugin.version>
        <maven-depends-plugin.version>1.2</maven-depends-plugin.version>
        <maven-compiler-plugin.version>3.3</maven-compiler-plugin.version>
        <javac.target>1.7</javac.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-core</artifactId>
            <version>${jmh.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-generator-annprocess</artifactId>
            <version>${jmh.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.ops4j.pax.exam</groupId>
            <artifactId>pax-exam</artifactId>
            <version>${pax-exam.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.ops4j.pax.exam</groupId>
            <artifactId>pax-exam-container-karaf</artifactId>
            <version>${pax-exam.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.ops4j.pax.exam</groupId>
            <artifactId>pax-exam-junit4</artifactId>
            <version>${pax-exam.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.servicemix</groupId>
            <artifactId>apache-servicemix</artifactId>
            <version>${apache-servicemix.version}</version>
            <scope>test</scope>
            <type>zip</type>
        </dependency>
        <dependency>
            <groupId>org.ops4j.pax.tinybundles</groupId>
            <artifactId>tinybundles</artifactId>
            <version>${tinybundles.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.servicemix.tooling</groupId>
                <artifactId>depends-maven-plugin</artifactId>
                <version>${maven-depends-plugin.version}</version>
                <executions>
                    <execution>
                        <id>generate-depends-file</id>
                        <goals>
                            <goal>generate-depends-file</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-sunfire-report-plugin.version}</version>
                <dependencies>
                    <dependency>
                        <groupId>org.apache.maven.surefire</groupId>
                        <artifactId>surefire-junit47</artifactId>
                        <version>${maven-sunfire-report-plugin.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${javac.target}</source>
                    <target>${javac.target}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

src/test/java/com/company/project/performance/MyBenchmarkTest.java:

package com.company.project.performance;

import static org.ops4j.pax.exam.CoreOptions.junitBundles;
import static org.ops4j.pax.exam.CoreOptions.maven;
import static org.ops4j.pax.exam.CoreOptions.options;
import static org.ops4j.pax.exam.CoreOptions.streamBundle;
import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle;
import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;

import java.io.File;
import java.io.FileInputStream;
import java.util.concurrent.TimeUnit;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.MavenUtils;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.options.MavenArtifactUrlReference;

@RunWith(PaxExam.class)
public class MyBenchmarkTest
{
    public static final String BENCHMARK_LIST = "META-INF/BenchmarkList";

    @Benchmark
    public void measureThroughput() throws InterruptedException
    {
        TimeUnit.MILLISECONDS.sleep(100);
    }

    @Configuration
    public Option[] config() throws Exception
    {
        String karafVersion = MavenUtils.getArtifactVersion("org.apache.karaf", "apache-karaf");
        MavenArtifactUrlReference servicemixUrl = maven()
                                                    .groupId("org.apache.servicemix")
                                                    .artifactId("apache-servicemix")
                                                    .versionAsInProject()
                                                    .type("zip");

        return options(karafDistributionConfiguration()
                         .frameworkUrl(servicemixUrl)
                         .useDeployFolder(false)
                         .karafVersion(karafVersion)
                         .unpackDirectory(new File("target/exam")),
                       keepRuntimeFolder(),
                       junitBundles(),
                       wrappedBundle(maven("org.openjdk.jmh", "jmh-core")),
                       streamBundle(bundle()
                                      .add(BENCHMARK_LIST, new FileInputStream("target/test-classes/" + BENCHMARK_LIST))
                                      .build(withBnd())));
    }

    @Test
    public void launchBenchmark() throws Exception
    {
        Options opts = new OptionsBuilder()
                             .include("com.company.project.performance.*")
                             .warmupIterations(1)
                             .forks(1)
                             .build();
        new Runner(opts).run();
    }
}

Upvotes: 2

Views: 879

Answers (1)

Achim Nierbeck
Achim Nierbeck

Reputation: 5285

The issue is clearly an import export issue. As your test which is using your meta data, isn't aware of those extra data. The bundle you generated with the streamBundle, needs to add an extra header information actually exporting those extra data.

streamBundle(bundle()
   .add(BENCHMARK_LIST, new FileInputStream("target/test-classes/" + BENCHMARK_LIST))
   .set(Constants.EXPORT_PACKAGE, BENCHMARK_LIST)

and in your test you'll need to make sure you are actually importing it.

@ProbeBuilder
public TestProbeBuilder probeConfiguration(TestProbeBuilder probe) {
    //make sure the needed imports are there. 
    probe.setHeader(Constants.IMPORT_PACKAGE, "*,"+BENCHMARK_LIST);
    return probe;
}

on the other hand it might just be a lot better to actually try to add those extra data into the test-bundle (your test class is generated into a bundle on the fly) Therefore the following should be added to the configuration

.metaInfResource(BENCHMARK_LIST)

Upvotes: 1

Related Questions