When building up search engines, indexing tons of data into a schema-less, distributed data store, Elasticsearch has always been a favourite tool of mine.

In addition to its core features, it also offers tools and documentation for us developers when we need to write integration tests for our Elasticsearch powered Java applications.

In the following tutorial I’d like to demonstrate how to implement a small sample application using Elasticsearch under the hood and how to write integration-tests with these tools for this application afterwards.

run elasticsearch integration test intellij
Figure 1. Run Elasticsearch Integration Test in IntelliJ IDE

Dependencies

Besides standard testing libraries like JUnit (junit:junig:4.12) and Hamcrest Matchers (org.hamcrest:hamcrest-all:1.3) we’re adding the following dependencies to our project’s pom.xml:

  • Elasticsearch because we’re using it in our sample application

  • Elasticsearch Test for using Elasticsearch clusters/nodes in our test environment

  • Randomizedtesting Runner to add some randomness to our tests .. e.g. the number of shards used for indices in our integration tests is randomized unless we’re overriding it. More information about randomized testing here.

  • Lucene Test Framework: Tools for Lucene testing .. mocks, codecs etc..

<dependency>
  <groupId>com.carrotsearch.randomizedtesting</groupId>
  <artifactId>randomizedtesting-runner</artifactId>
  <version>2.3.4</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.lucene</groupId>
  <artifactId>lucene-test-framework</artifactId>
  <version>5.5.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.elasticsearch</groupId>
  <artifactId>elasticsearch</artifactId>
  <version>2.3.4</version>
  <scope>test</scope>
  <type>test-jar</type>
</dependency>
<dependency>
  <groupId>org.elasticsearch</groupId>
  <artifactId>elasticsearch</artifactId>
  <version>2.3.4</version>
</dependency>

My complete Maven configuration is available here.

Search Application

Out sample application allows us to manage different beers so that we’re able to save beers to the search index and to search beers.

Out beer search creates an index named drinks and stores beers there. In addition it allows us to search beers by their tag.

We’re mapping between the JSON representation of a beer entity and the corresponding Java ™ class here.

package com.hascode.tutorial.control;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHits;

import com.hascode.tutorial.entity.Beer;

public class BeerSearch {
    private static final String TYPE_BEER = "beer";
    private static final String INDEX = "drinks";

    private final Client client;

    public BeerSearch(Client client) {
        this.client = client;
    }

    public void add(Beer beer) {
        System.out.printf("adding beer to search index: %s\n", beer);
        IndexRequest indexRequest = new IndexRequest(INDEX, TYPE_BEER, beer.getId());
        indexRequest.source(beer.toJson());
        IndexResponse response = client.index(indexRequest).actionGet();
        System.out.printf("entry added to index '%s', type '%s', doc-version: '%s', doc-id: '%s', created: %s\n",
                response.getIndex(), response.getType(), response.getVersion(), response.getId(), response.isCreated());
    }

    public List<Beer> findByTag(String tag) {
        System.out.printf("searching beers for given tag: %s\n", tag);
        SearchResponse response = client.prepareSearch(INDEX).setTypes(TYPE_BEER).addFields("name", "tags")
                .setQuery(QueryBuilders.termQuery("tags", tag)).execute().actionGet();
        SearchHits hits = response.getHits();
        System.out.printf("%s hits for tag '%s' found\n", hits.totalHits(), tag);

        return StreamSupport.stream(hits.spliterator(), true).map(hit -> {
            String name = hit.field("name").getValue();
            String[] tags = hit.field("tags").getValues().toArray(new String[] {});
            return new Beer(hit.getId(), name, tags);
        }).collect(Collectors.toList());
    }

}

Beer Entity

Our beer entity is a simple POJO with three attributes .. id, name and an array of tags, getters and setters and one method to convert the beer entity to a JSON representation.

I didn’t want to add further dependencies here so I’ve implemented a simple JSON conversion by hand.

package com.hascode.tutorial.entity;

import java.util.Arrays;

public class Beer {
    private final String id;
    private final String name;
    private final String[] tags;

    public Beer(String id, String name, String... tags) {
        this.id = id;
        this.name = name;
        this.tags = tags.clone();
    }

    public String toJson() {
        return String.format("{\"name\":\"%s\",\"tags\":[%s]}", name,
                Arrays.stream(tags).map(tag -> "\"" + tag + "\"").reduce((p, c) -> p + ", " + c).get());
    }

    // getters, toString ommitted ...
}

The following application demonstrates the usage of our beer search, we simply need to boot an Elasticsearch node in a temporary directory and pass the client instance to our beer search.

We’re adding four beers to our search and afterwards search for all beers matching the tag "tasty" and print them to STDOUT.

package com.hascode.tutorial.boundary;

import java.io.File;
import java.util.List;

import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeBuilder;

import com.google.common.io.Files;
import com.hascode.tutorial.control.BeerSearch;
import com.hascode.tutorial.entity.Beer;

public class Main {

    public static void main(String[] args) throws InterruptedException {
        File tempDir = Files.createTempDir();
        Settings settings = Settings.builder().put("path.home", tempDir.getAbsolutePath()).build();
        Node server = NodeBuilder.nodeBuilder().settings(settings).build();
        final String clusterName = server.settings().get("cluster.name");

        System.out.printf("starting server with cluster-name: %s\n", clusterName);
        server.start();
        Thread.sleep(2000);
        Client client = server.client();

        BeerSearch search = new BeerSearch(client);
        search.add(new Beer("1", "Becks", "mild", "tasty"));
        search.add(new Beer("2", "Holsten", "crisp", "strong"));
        search.add(new Beer("3", "Kilkenny", "mild", "sweet"));
        search.add(new Beer("4", "Budvar", "tasty", "crispy"));

        Thread.sleep(2000);
        List<Beer> beers = search.findByTag("tasty");
        beers.forEach(System.out::println);

        System.out.printf("closing server with cluster-name: %s\n", clusterName);
        server.close();
    }

}

Running the application above should produce a similar output, here it is run using Maven in the command line:

$ mvn exec:java -Dexec.mainClass=com.hascode.tutorial.boundary.Main                                                                                                                                                                                                      1 ↵
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building elasticsearch-testing 1.0.0
[INFO] ------------------------------------------------------------------------
[..]
INFO: [Thanos] initializing ...
[..]
INFO: [Thanos] using [1] data paths, mounts [[/ (/dev/sda5)]], net usable_space [9.1gb], net total_space [235.3gb], spins? [no], types [ext4]
[..]
INFO: [Thanos] started
Aug 22, 2016 6:53:50 PM org.elasticsearch.gateway.GatewayService$GatewayRecoveryListener$1 clusterStateProcessed
INFO: [Thanos] recovered [0] indices into cluster_state
adding beer to search index: Beer [id=1, name=Becks, tags=[mild, tasty]]
Aug 22, 2016 6:53:52 PM org.elasticsearch.cluster.metadata.MetaDataCreateIndexService$1 execute
INFO: [Thanos] [drinks] creating index, cause [auto(index api)], templates [], shards [5]/[1], mappings [beer]
Aug 22, 2016 6:53:53 PM org.elasticsearch.cluster.routing.allocation.AllocationService logClusterHealthStateChange
INFO: [Thanos] Cluster health status changed from [RED] to [YELLOW] (reason: [shards started [[drinks][4]] ...]).
Aug 22, 2016 6:53:53 PM org.elasticsearch.cluster.metadata.MetaDataMappingService$PutMappingExecutor applyRequest
INFO: [Thanos] [drinks] update_mapping [beer]
entry added to index 'drinks', type 'beer', doc-version: '1', doc-id: '1', created: true
adding beer to search index: Beer [id=2, name=Holsten, tags=[crisp, strong]]
entry added to index 'drinks', type 'beer', doc-version: '1', doc-id: '2', created: true
adding beer to search index: Beer [id=3, name=Kilkenny, tags=[mild, sweet]]
entry added to index 'drinks', type 'beer', doc-version: '1', doc-id: '3', created: true
adding beer to search index: Beer [id=4, name=Budvar, tags=[tasty, crispy]]
entry added to index 'drinks', type 'beer', doc-version: '1', doc-id: '4', created: true
searching beers for given tag: tasty
2 hits for tag 'tasty' found
Beer [id=4, name=Budvar, tags=[tasty, crispy]]
Beer [id=1, name=Becks, tags=[mild, tasty]]
closing server with cluster-name: elasticsearch
[..]
INFO: [Thanos] closed
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

Writing Integration Tests

In the following simple integration test, we’re extending our test-class from ESIntegTestCase as this not only adds life-cycle control for our test-search cluster but also the following accessors and verification methods:

  • admin(): Return an admin client to perform administrative tasks

  • cluster(): Return a test cluster manager

  • clusterService(): Returns a cluster manager to fine tune out cluster service

  • createIndex(): Creates an index with a given name

  • ensureGreen(): Ensures that our cluster’s state is green (30 secs timeout before failing)

  • ensureYellow(): Ensures that our cluster’s state is yellow (30 secs timeout before failing)

  • flush(): Flushes our cluster’s indices

  • flushAndRefresh(): Calls flush() and refresh()

  • forceMerge(): Waits for relocations and force merges all indices in the cluster

  • indexExists(): Verify an index for given name exists

  • refresh(): Refreshes our cluster indices

More detailed information is available in the Elasticsearch Guide here.

In our test we’re now executing the following steps:

  • obtain a reference to the search client

  • initialize our beer search with the test’s search client

  • add two beers to the search

  • refresh the cluster’s indices so that we may search immediatly

  • verify that an index named drinks exists

  • verify that the index named drinks status is green

  • searching beers by a tag

  • verify the result

package it;

import static org.hamcrest.Matchers.equalTo;

import java.util.List;

import org.elasticsearch.client.Client;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.ESIntegTestCase.Scope;
import org.junit.Test;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
import com.hascode.tutorial.control.BeerSearch;
import com.hascode.tutorial.entity.Beer;

@ClusterScope(scope = Scope.SUITE)
@ThreadLeakScope(ThreadLeakScope.Scope.NONE)
public class BeerSearchIntegrationTest extends ESIntegTestCase {
    @Test
    public void shouldIndexAndSearchBeers() throws Exception {
        Client client = client();

        BeerSearch search = new BeerSearch(client);
        search.add(new Beer("1", "Becks", "mild", "tasty"));
        search.add(new Beer("2", "Holsten", "crisp", "strong"));

        refresh(); // otherwise we would not find beers yet

        indexExists("drinks"); // verifies that index 'drinks' exists
        ensureGreen("drinks"); // ensures cluster status is green

        List<Beer> strongBeers = search.findByTag("strong");
        assertThat(strongBeers.size(), equalTo(1));

        Beer strongBeer = strongBeers.get(0);
        assertThat(strongBeer.getName(), equalTo("Holsten"));
    }
}

Running the Tests

We may now run the test using our favourite IDE or using Maven in the command-line..

Running in IntelliJ IDE

run elasticsearch integration test intellij
Figure 2. Run Elasticsearch Integration Test in IntelliJ IDE

Running with Maven

$ mvn integration-test
[..]
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running it.BeerSearchIntegrationTest
[..]
Srp 22, 2016 7:58:05 ODP. org.elasticsearch.test.ESIntegTestCase printTestMessage
INFO: [BeerSearchIntegrationTest#shouldIndexAndSearchBeers]: setup test
Srp 22, 2016 7:58:05 ODP. org.elasticsearch.test.InternalTestCluster <init>
INFO: Setup InternalTestCluster [SUITE-CHILD_VM=[0]-CLUSTER_SEED=[-4660392254476517275]-HASH=[31B512F53F9]-cluster] with seed [BF52F5EFB5F20065] using [1] data nodes and [1] client nodes
[..]
INFO: [BeerSearchIntegrationTest#shouldIndexAndSearchBeers]: starting test
Srp 22, 2016 7:58:06 ODP. org.elasticsearch.plugins.PluginsService <init>
INFO: [transport_client_node_s1] modules [], plugins [], sites []
Srp 22, 2016 7:58:06 ODP. org.elasticsearch.transport.TransportService doStart
INFO: [transport_client_node_s1] publish_address {local[4]}, bound_addresses {local[4]}
adding beer to search index: Beer [id=1, name=Becks, tags=[mild, tasty]]
Srp 22, 2016 7:58:06 ODP. org.elasticsearch.cluster.metadata.MetaDataCreateIndexService$1 execute
INFO: [node_s0] [drinks] creating index, cause [auto(index api)], templates [random_index_template], shards [10]/[0], mappings [_default_, beer]
Srp 22, 2016 7:58:07 ODP. org.elasticsearch.cluster.routing.allocation.AllocationService logClusterHealthStateChange
INFO: [node_s0] Cluster health status changed from [RED] to [GREEN] (reason: [shards started [[drinks][7], [drinks][7]] ...]).
Srp 22, 2016 7:58:07 ODP. org.elasticsearch.cluster.metadata.MetaDataMappingService$PutMappingExecutor applyRequest
INFO: [node_s0] [drinks] update_mapping [beer]
entry added to index 'drinks', type 'beer', doc-version: '1', doc-id: '1', created: true
adding beer to search index: Beer [id=2, name=Holsten, tags=[crisp, strong]]
entry added to index 'drinks', type 'beer', doc-version: '1', doc-id: '2', created: true
searching beers for given tag: strong
1 hits for tag 'strong' found
Srp 22, 2016 7:58:07 ODP. org.elasticsearch.test.ESIntegTestCase printTestMessage
INFO: [BeerSearchIntegrationTest#shouldIndexAndSearchBeers]: finished test
Srp 22, 2016 7:58:07 ODP. org.elasticsearch.test.ESIntegTestCase printTestMessage
INFO: [BeerSearchIntegrationTest#shouldIndexAndSearchBeers]: cleaning up after test
[..]
Srp 22, 2016 7:58:07 ODP. org.elasticsearch.node.Node close
INFO: [node_s1] closed
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.968 sec - in it.BeerSearchIntegrationTest

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[..]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.158s

Tutorial Sources

Please feel free to download the tutorial sources from my GitHub repository, fork it there or clone it using Git:

git clone https://github.com/hascode/elasticsearch-testing-tutorial.git

Troubleshooting

  • "Exception in thread "main" java.lang.IllegalStateException: path.home is not configured
    at org.elasticsearch.env.Environment.<init>(Environment.java:101)
    at org.elasticsearch.node.internal.InternalSettingsPreparer.prepareEnvironment(InternalSettingsPreparer.java:81)
    at org.elasticsearch.node.Node.<init>(Node.java:140)
    at org.elasticsearch.node.NodeBuilder.build(NodeBuilder.java:143)
    at com.hascode.tutorial.Main.main(Main.java:9)
    ": We need to configure the home-path for Elasticsearch, e.g. by using the SettingsBuilder like this:

    Settings settings = Settings.builder().put("path.home", "/tmp").build();
    Node server = NodeBuilder.nodeBuilder().settings(settings).build();
  • "Exception in thread "main" java.lang.NoSuchFieldError: LUCENE_4_0_0
    at org.elasticsearch.Version.<clinit>(Version.java:44)
    at org.elasticsearch.node.Node.<init>(Node.java:140)
    at org.elasticsearch.node.NodeBuilder.build(NodeBuilder.java:143)
    at com.hascode.tutorial.Main.main(Main.java:11)
    ": Lucene version conflict between Elasticsearch Test and Lucene Test, align both versions in the pom.xml.

  • "Aug 17, 2016 6:19:56 PM org.elasticsearch.bootstrap.Natives <clinit>
    WARNING: JNA not found. native methods will be disabled.
    java.lang.ClassNotFoundException: com.sun.jna.Native
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at org.elasticsearch.bootstrap.Natives.<clinit>(Natives.java:45)
    at org.elasticsearch.bootstrap.BootstrapInfo.isMemoryLocked(BootstrapInfo.java:44)
    at org.elasticsearch.monitor.process.ProcessProbe.processInfo(ProcessProbe.java:130)
    at org.elasticsearch.monitor.process.ProcessService.<init>(ProcessService.java:44)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at org.elasticsearch.common.inject.DefaultConstructionProxyFactory$1.newInstance(DefaultConstructionProxyFactory.java:50)
    at org.elasticsearch.common.inject.ConstructorInjector.construct(ConstructorInjector.java:86)
    at org.elasticsearch.common.inject.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:104)
    at org.elasticsearch.common.inject.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:47)
    at org.elasticsearch.common.inject.InjectorImpl.callInContext(InjectorImpl.java:886)
    at org.elasticsearch.common.inject.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:43)
    at org.elasticsearch.common.inject.Scopes$1$1.get(Scopes.java:59)
    at org.elasticsearch.common.inject.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:46)
    at org.elasticsearch.common.inject.SingleParameterInjector.inject(SingleParameterInjector.java:42)
    at org.elasticsearch.common.inject.SingleParameterInjector.getAll(SingleParameterInjector.java:66)
    at org.elasticsearch.common.inject.ConstructorInjector.construct(ConstructorInjector.java:85)
    at org.elasticsearch.common.inject.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:104)
    at org.elasticsearch.common.inject.SingleParameterInjector.inject(SingleParameterInjector.java:42)
    at org.elasticsearch.common.inject.SingleParameterInjector.getAll(SingleParameterInjector.java:66)
    at org.elasticsearch.common.inject.ConstructorInjector.construct(ConstructorInjector.java:85)
    at org.elasticsearch.common.inject.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:104)
    at org.elasticsearch.common.inject.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:47)
    at org.elasticsearch.common.inject.InjectorImpl.callInContext(InjectorImpl.java:886)
    at org.elasticsearch.common.inject.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:43)
    at org.elasticsearch.common.inject.Scopes$1$1.get(Scopes.java:59)
    at org.elasticsearch.common.inject.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:46)
    at org.elasticsearch.common.inject.InjectorBuilder$1.call(InjectorBuilder.java:201)
    at org.elasticsearch.common.inject.InjectorBuilder$1.call(InjectorBuilder.java:193)
    at org.elasticsearch.common.inject.InjectorImpl.callInContext(InjectorImpl.java:879)
    at org.elasticsearch.common.inject.InjectorBuilder.loadEagerSingletons(InjectorBuilder.java:193)
    at org.elasticsearch.common.inject.InjectorBuilder.injectDynamically(InjectorBuilder.java:175)
    at org.elasticsearch.common.inject.InjectorBuilder.build(InjectorBuilder.java:110)
    at org.elasticsearch.common.inject.Guice.createInjector(Guice.java:96)
    at org.elasticsearch.common.inject.Guice.createInjector(Guice.java:70)
    at org.elasticsearch.common.inject.ModulesBuilder.createInjector(ModulesBuilder.java:46)
    at org.elasticsearch.node.Node.<init>(Node.java:213)
    at org.elasticsearch.node.Node.<init>(Node.java:140)
    at org.elasticsearch.node.NodeBuilder.build(NodeBuilder.java:143)
    at com.hascode.tutorial.Main.main(Main.java:11)
    ": Native API is not available, this exception does not hurt our tests most times, but we may avoid it by adding JNA by hand

    <dependency>
        <groupId>net.java.dev.jna</groupId>
        <artifactId>jna</artifactId>
        <version>4.2.2</version>
    </dependency>
  • "java.lang.RuntimeException: found jar hell in test classpath
    at org.elasticsearch.bootstrap.BootstrapForTesting.<clinit>(BootstrapForTesting.java:95)
    at org.elasticsearch.test.ESTestCase.<clinit>(ESTestCase.java:99)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at com.carrotsearch.randomizedtesting.RandomizedRunner$2.run(RandomizedRunner.java:585)
    Caused by: java.lang.IllegalStateException: jar hell!
    class: org.hamcrest.BaseDescription
    jar1: /home/private/soma/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
    jar2: /home/private/soma/.m2/repository/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3.jar
    at org.elasticsearch.bootstrap.JarHell.checkClass(JarHell.java:282)
    at org.elasticsearch.bootstrap.JarHell.checkJarHell(JarHell.java:186)
    at org.elasticsearch.bootstrap.JarHell.checkJarHell(JarHell.java:87)
    at org.elasticsearch.bootstrap.BootstrapForTesting.<clinit>(BootstrapForTesting.java:93)
    … 4 more
    ": Conflicting versions of a dependency found in classpath .. in this case, hamcrest-core a transitive dependency from junit conflicted with hamcrest-all so adding the following exclusion solved the problem here:

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.hamcrest</groupId>
          <artifactId>hamcrest-core</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  • "java.security.AccessControlException: access denied ("org.elasticsearch.ThreadPermission" "modifyArbitraryThreadGroup")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:457)
    at java.security.AccessController.checkPermission(AccessController.java:884)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at org.elasticsearch.SecureSM.checkThreadGroupAccess(SecureSM.java:166)
    at org.elasticsearch.SecureSM.checkAccess(SecureSM.java:113)
    at java.lang.ThreadGroup.checkAccess(ThreadGroup.java:315)
    at java.lang.ThreadGroup.getParent(ThreadGroup.java:167)
    at com.carrotsearch.randomizedtesting.Threads$2.run(Threads.java:127)
    at com.carrotsearch.randomizedtesting.Threads$2.run(Threads.java:123)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.carrotsearch.randomizedtesting.Threads.getTopThreadGroup(Threads.java:123)
    at com.carrotsearch.randomizedtesting.Threads.getAllThreads(Threads.java:99)
    at com.carrotsearch.randomizedtesting.ThreadLeakControl.(ThreadLeakControl.java:348)
    at com.carrotsearch.randomizedtesting.RandomizedRunner.runSuite(RandomizedRunner.java:673)
    at com.carrotsearch.randomizedtesting.RandomizedRunner.access$200(RandomizedRunner.java:140)
    at com.carrotsearch.randomizedtesting.RandomizedRunner$2.run(RandomizedRunner.java:591)OR ALSO…REPRODUCE WITH: mvn test -Pdev -Dtests.seed=ACE4CC07E365B757 -Dtests.class=it.SomeIntegrationTest -Dtests.locale=en-US -Dtests.timezone=Europe/Berlin
    Aug 22, 2016 6:10:18 PM com.carrotsearch.randomizedtesting.RandomizedRunner runSuite
    SEVERE: Panic: RunListener hook shouldn’t throw exceptions.
    java.lang.NullPointerException
    at org.apache.lucene.util.RunListenerPrintReproduceInfo.printDebuggingInformation(RunListenerPrintReproduceInfo.java:129)
    at org.apache.lucene.util.RunListenerPrintReproduceInfo.testRunFinished(RunListenerPrintReproduceInfo.java:118)
    at com.carrotsearch.randomizedtesting.RandomizedRunner.runSuite(RandomizedRunner.java:706)
    at com.carrotsearch.randomizedtesting.RandomizedRunner.access$200(RandomizedRunner.java:140)
    at com.carrotsearch.randomizedtesting.RandomizedRunner$2.run(RandomizedRunner.java:591)

    Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.411 sec <<< FAILURE! – in it.SomeIntegrationTest
    it.SomeIntegrationTest Time elapsed: 0.4 sec <<< ERROR!
    java.security.AccessControlException: access denied ("org.elasticsearch.ThreadPermission" "modifyArbitraryThreadGroup")

    it.SomeIntegrationTest Time elapsed: 0.411 sec <<< ERROR!
    java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "setDefaultUncaughtExceptionHandler")
    at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:367)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:274)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:238)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:161)

    Running ut.SomeUnitTest
    Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.001 sec <<< FAILURE! – in ut.SomeUnitTest
    ut.SomeUnitTest Time elapsed: 0.001 sec <<< ERROR! java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "setDefaultUncaughtExceptionHandler") at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:367) at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:274) at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:238) at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:161) Results : Tests in error: SomeIntegrationTest.it.SomeIntegrationTest » AccessControl access denied ("org… JUnit4Provider.invoke:161→executeTestSet:238→executeWithRerun:274→execute:367 » AccessControl
    JUnit4Provider.invoke:161→executeTestSet:238→executeWithRerun:274→execute:367 » AccessControl

    Tests run: 3, Failures: 0, Errors: 3, Skipped: 0": We need to disable the security manager by adding an argument -Dtests.security.manager=false. In my example I have added it to the execution of the Maven Surefire Plugin that is responsible for running our tests like this:

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.19.1</version>
      <configuration>
        <argLine>-Dtests.security.manager=false</argLine>
      </configuration>
    </plugin>
  • "Aug 22, 2016 6:17:17 PM com.carrotsearch.randomizedtesting.ThreadLeakControl checkThreadLeaks
    WARNING: Will linger awaiting termination of 1 leaked thread(s).
    Aug 22, 2016 6:17:22 PM com.carrotsearch.randomizedtesting.ThreadLeakControl checkThreadLeaks
    SEVERE: 1 thread leaked from SUITE scope at it.SomeIntegrationTest:
    1) Thread[id=67, name=ForkJoinPool.commonPool-worker-1, state=TIMED_WAITING, group=TGRP-SomeIntegrationTest]
    at sun.misc.Unsafe.park(Native Method)
    at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1821)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1690)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
    Aug 22, 2016 6:17:22 PM com.carrotsearch.randomizedtesting.ThreadLeakControl tryToInterruptAll
    INFO: Starting to interrupt leaked threads:
    1) Thread[id=67, name=ForkJoinPool.commonPool-worker-1, state=TIMED_WAITING, group=TGRP-SomeIntegrationTest]
    Aug 22, 2016 6:17:25 PM com.carrotsearch.randomizedtesting.ThreadLeakControl tryToInterruptAll
    SEVERE: There are still zombie threads that couldn’t be terminated:
    1) Thread[id=67, name=ForkJoinPool.commonPool-worker-1, state=TIMED_WAITING, group=TGRP-SomeIntegrationTest]
    at sun.misc.Unsafe.park(Native Method)
    at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1821)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1690)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
    REPRODUCE WITH: mvn test -Pdev -Dtests.seed=FFAE0B683AB0F9E5 -Dtests.class=it.SomeIntegrationTest -Dtests.security.manager=false -Dtests.locale=en-US -Dtests.timezone=Europe/Berlin
    REPRODUCE WITH: mvn test -Pdev -Dtests.seed=FFAE0B683AB0F9E5 -Dtests.class=it.SomeIntegrationTest -Dtests.security.manager=false -Dtests.locale=en-US -Dtests.timezone=Europe/Berlin
    NOTE: test params are: codec=Asserting(Lucene54): \{_field_names=PostingsFormat(name=Asserting), _type=PostingsFormat(name=Asserting), name=PostingsFormat(name=Asserting), _timestamp=PostingsFormat(name=Asserting), _uid=PostingsFormat(name=Asserting), _all=PostingsFormat(name=Asserting), tags=PostingsFormat(name=Asserting)}, docValues:\{_type=DocValuesFormat(name=Asserting), _timestamp=DocValuesFormat(name=Lucene54), _version=DocValuesFormat(name=Asserting)}, sim=RandomSimilarity(queryNorm=true,coord=no): \{}, locale=fr-LU, timezone=Pacific/Marquesas
    NOTE: Linux 3.2.0-23-generic amd64/Oracle Corporation 1.8.0_45 (64-bit)/cpus=8,threads=2,free=157435600,total=241696768
    NOTE: All tests run in this JVM: [SomeIntegrationTest]
    Tests run: 3, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 12.84 sec <<< FAILURE! – in it.SomeIntegrationTest
    it.SomeIntegrationTest  Time elapsed: 12.389 sec  <<< ERROR!
    com.carrotsearch.randomizedtesting.ThreadLeakError:
    1 thread leaked from SUITE scope at it.SomeIntegrationTest:
    1) Thread[id=67, name=ForkJoinPool.commonPool-worker-1, state=TIMED_WAITING, group=TGRP-SomeIntegrationTest]
    at sun.misc.Unsafe.park(Native Method)
    at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1821)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1690)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)it.SomeIntegrationTest  Time elapsed: 12.39 sec  <<< ERROR!
    com.carrotsearch.randomizedtesting.ThreadLeakError:
    There are still zombie threads that couldn’t be terminated:
    1) Thread[id=67, name=ForkJoinPool.commonPool-worker-1, state=TIMED_WAITING, group=TGRP-SomeIntegrationTest]
    at sun.misc.Unsafe.park(Native Method)
    at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1821)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1690)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

    Running ut.SomeUnitTest
    Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 sec – in ut.SomeUnitTest

    Results :

    Tests in error:
    it.SomeIntegrationTest.it.SomeIntegrationTest
    Run 1: SomeIntegrationTest.it.SomeIntegrationTest » ThreadLeak 1 thread leaked from S…
    Run 2: SomeIntegrationTest.it.SomeIntegrationTest » ThreadLeak There are still zombie…
    ": The leak detection is quite strict here .. adding a Thread.sleep() to our BeerSearch might suffice to produce this error. We may bypass this by adding the following class level annotation to our integration test: @ThreadLeakScope(ThreadLeakScope.Scope.NONE)