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.
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.
Beer Search
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 ...
}
Running the Beer Search
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
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
Resources
Other Articles of mine
Also feel free to to take a look at other blog articles of mine about search engines/integrations/libraries:
-
Lucene by Example: Specifying Analyzers on a per-field-basis and writing a custom Analyzer/Tokenizer
-
Creating elegant, typesafe Queries for JPA, mongoDB/Morphia and Lucene using Querydsl
-
Content Detection, Metadata and Content Extraction with Apache Tika
-
Hibernate Search Faceting: Discrete and Range Faceting by Example
-
JPA Persistence and Lucene Indexing combined in Hibernate Search
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 » AccessControlTests 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.SomeUnitTestResults :
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)