/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed 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.drools.testcoverage.regression; import org.assertj.core.api.Assertions; import org.drools.testcoverage.common.model.Message; import org.drools.testcoverage.common.model.Person; import org.drools.testcoverage.common.util.KieBaseUtil; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.kie.api.KieBase; import org.kie.api.KieServices; import org.kie.api.command.Command; import org.kie.api.command.KieCommands; import org.kie.api.io.Resource; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.StatelessKieSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Test to verify BRMS-532 (Drools Session insert * ConcurrentModificationException in Multithreading Environment) is fixed */ public class SessionInsertMultiThreadingTest { private static final Logger LOGGER = LoggerFactory.getLogger(SessionInsertMultiThreadingTest.class); private static final int THREADS = 50; private static final int RUNS_PER_THREAD = 100; private static KieBase kbase; private static ExecutorService executor; @BeforeClass public static void createKbase() throws Exception { final Resource resource = KieServices.Factory.get().getResources().newClassPathResource( "sessionInsertMultithreadingTest.drl", SessionInsertMultiThreadingTest.class); kbase = KieBaseUtil.getKieBaseFromResources(true, resource); } @Before public void createExecutor() { executor = Executors.newFixedThreadPool(THREADS); } @After public void shutdownExecutor() throws Exception { if (kbase != null) { for (KieSession ksession : kbase.getKieSessions()) { ksession.dispose(); } } executor.shutdown(); if (!executor.awaitTermination(30, TimeUnit.SECONDS)) { LOGGER.warn("Executor not shut down in 30s!"); executor.shutdownNow(); } executor = null; } @Test public void testCommonBase() throws Exception { final List<Future<?>> futures = new ArrayList<Future<?>>(); for (int i = 0; i < RUNS_PER_THREAD; i++) { for (int j = 0; j < THREADS; j++) { futures.add(executor.submit(new KieBaseRunnable(kbase))); } } waitForCompletion(futures); } @Test public void testCommonSession() throws Exception { for (int i = 0; i < RUNS_PER_THREAD; i++) { testSingleCommonSession(); } } private void testSingleCommonSession() throws Exception { final List<Future<?>> futures = new ArrayList<Future<?>>(); final KieSession ksession = kbase.newKieSession(); try { runTestBySeveralThreads(ksession, futures); waitForCompletion(futures); } finally { if (ksession != null) { ksession.dispose(); } } } /** * Reproducer for BZ 1187070. */ @Test public void testCommonStatelessSessionBZ1187070() throws Exception { for (int i = 0; i < RUNS_PER_THREAD; i++) { testSingleCommonStatelessSession(); } } private void testSingleCommonStatelessSession() throws Exception { final List<Future<?>> futures = new ArrayList<Future<?>>(); final StatelessKieSession statelessKieSession = kbase.newStatelessKieSession(); runTestBySeveralThreads(statelessKieSession, futures); waitForCompletion(futures); } private void runTestBySeveralThreads(final KieSession ksession, final List<Future<?>> futures) throws Exception { for (int j = 0; j < THREADS; j++) { futures.add(executor.submit(new KieSessionRunnable(ksession))); } } private void runTestBySeveralThreads(final StatelessKieSession statelessKieSession, final List<Future<?>> futures) throws Exception { for (int j = 0; j < THREADS; j++) { futures.add(executor.submit(new StatelessKieSessionRunnable(statelessKieSession))); } } private void waitForCompletion(final List<Future<?>> futures) throws Exception { Exception lastException = null; for (Future<?> future : futures) { try { future.get(10, TimeUnit.SECONDS); } catch (Exception e) { e.printStackTrace(); lastException = e; } } if (lastException != null) { throw lastException; } } /** * The Runnable performing the test on a given shared StatelessKieSession. */ public static class StatelessKieSessionRunnable implements Runnable { protected final StatelessKieSession statelessKieSession; public StatelessKieSessionRunnable(StatelessKieSession statelessKieSession) { this.statelessKieSession = statelessKieSession; } @Override public void run() { final Message m = new Message(); final Person p = new Person(); final KieCommands kieCommands = KieServices.Factory.get().getCommands(); final List<Command<?>> commandList = new ArrayList<Command<?>>(); commandList.add(kieCommands.newInsert(m)); commandList.add(kieCommands.newInsert(p)); commandList.add(kieCommands.newFireAllRules()); statelessKieSession.execute(kieCommands.newBatchExecution(commandList)); Assertions.assertThat(p.getName()).isNotNull(); Assertions.assertThat(m.getMessage()).isNotNull(); } } /** * The Runnable performing the test on a given shared KieSession. */ public static class KieSessionRunnable implements Runnable { protected final KieSession ksession; public KieSessionRunnable(KieSession ksession) { this.ksession = ksession; } @Override public void run() { final Message m = new Message(); final Person p = new Person(); ksession.insert(m); ksession.insert(p); ksession.fireAllRules(); Assertions.assertThat(p.getName()).isNotNull(); Assertions.assertThat(m.getMessage()).isNotNull(); } } /** * The Runnable performing the test on a given shared KieBase. */ public static class KieBaseRunnable extends KieSessionRunnable { public KieBaseRunnable(KieBase kbase) { super(kbase.newKieSession()); } @Override public void run() { try { super.run(); } finally { ksession.dispose(); } } } }