/** * Licensed to the Austrian Association for Software Tool Integration (AASTI) * under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. The AASTI licenses this file to you 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.openengsb.itests.exam; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; import static org.ops4j.pax.exam.OptionUtils.combine; import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFileExtend; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.openengsb.core.api.context.ContextHolder; import org.openengsb.core.api.model.ModelWrapper; import org.openengsb.core.edb.api.EDBObject; import org.openengsb.core.edb.api.EngineeringDatabaseService; import org.openengsb.core.ekb.api.EKBCommit; import org.openengsb.core.ekb.api.EKBConcurrentException; import org.openengsb.core.ekb.api.PersistInterface; import org.openengsb.core.ekb.api.QueryInterface; import org.openengsb.domain.example.model.SourceModelA; import org.openengsb.itests.util.AbstractModelUsingExamTestHelper; import org.ops4j.pax.exam.Configuration; import org.ops4j.pax.exam.Option; import org.ops4j.pax.exam.junit.PaxExam; import org.ops4j.pax.exam.karaf.options.KarafDistributionConfigurationFilePutOption; import org.ops4j.pax.exam.karaf.options.configs.FeaturesCfg; @RunWith(PaxExam.class) public class ConcurrentWriteAccessIT extends AbstractModelUsingExamTestHelper { private static final String CONTEXT = "testcontext"; private EngineeringDatabaseService edbService; private QueryInterface query; private PersistInterface persist; @Configuration public static Option[] myConfiguration() throws Exception { Option[] options = new Option[]{ new KarafDistributionConfigurationFilePutOption( "etc/org.openengsb.ekb.cfg", "modelUpdatePropagationMode", "DEACTIVATED"), new KarafDistributionConfigurationFilePutOption( "etc/org.openengsb.ekb.cfg", "persistInterfaceLockingMode", "ACTIVATED"), editConfigurationFileExtend(FeaturesCfg.BOOT, ",openengsb-connector-example") }; return combine(baseConfiguration(), options); } @Before public void setup() throws Exception { edbService = getOsgiService(EngineeringDatabaseService.class); query = getOsgiService(QueryInterface.class); persist = getOsgiService(PersistInterface.class); ContextHolder.get().setCurrentContextId(CONTEXT); } @Test(expected = EKBConcurrentException.class) public void testIfUnexpectedParentRevisionThrowsException_shouldThrowException() throws Exception { SourceModelA model = new SourceModelA(); model.setEdbId("unexpectedParentRevision/1"); EKBCommit commit = getTestEKBCommit().addInsert(model); persist.commit(commit, null); model = new SourceModelA(); model.setEdbId("unexpectedParentRevision/2"); commit = getTestEKBCommit().addInsert(model); // second time throws an exception since the expected parent revision is no longer null persist.commit(commit, null); } @Test(expected = EKBConcurrentException.class) public void testIfConcurrentWritingInTheSameContextThrowsAnException_shouldThrowException() throws Exception { EKBCommit commit = getTestEKBCommit(); for (int i = 0; i < 30; i++) { SourceModelA model = new SourceModelA(); model.setEdbId("concurrentTest/1/" + i); commit.addInsert(model); } EKBCommit anotherCommit = getTestEKBCommit(); SourceModelA model = new SourceModelA(); model.setEdbId("concurrentTest/1/30"); anotherCommit.addInsert(model); ModelCommitThread thread = new ModelCommitThread(persist, commit, CONTEXT); thread.start(); Thread.sleep(20); // throws an exception since the other commit is still running in the context persist.commit(anotherCommit); } @Test public void testIfConcurrentWritingInDifferentContextsWorks_shouldWork() throws Exception { String otherContext = "a_different_context"; String oid = ""; EKBCommit commit = getTestEKBCommit(); for (int i = 0; i < 15; i++) { SourceModelA model = new SourceModelA(); model.setEdbId("concurrentTest/2/" + i); commit.addInsert(model); if (i == 14) { oid = ModelWrapper.wrap(model).retrieveInternalModelId().toString(); } } ModelCommitThread thread = new ModelCommitThread(persist, commit, CONTEXT); thread.start(); ModelCommitThread thread2 = new ModelCommitThread(persist, commit, otherContext); thread2.start(); thread.join(); thread2.join(); ContextHolder.get().setCurrentContextId(CONTEXT); assertThat(edbService.getObject(getModelOid(oid)), notNullValue()); ContextHolder.get().setCurrentContextId(otherContext); assertThat(edbService.getObject(getModelOid(oid)), notNullValue()); } @Test public void testIfCheckForContextIsDoneCorrectly_shouldWork() throws Exception { ContextHolder.get().setCurrentContextId("A"); EKBCommit commit = getTestEKBCommit(); SourceModelA model = new SourceModelA(); model.setEdbId("contextSwitchTest/1"); String oid = ModelWrapper.wrap(model).retrieveInternalModelId().toString(); model.setName("A"); commit.addInsert(model); persist.commit(commit, query.getLastRevisionNumberOfContext("A")); ContextHolder.get().setCurrentContextId("B"); persist.commit(commit, query.getLastRevisionNumberOfContext("B")); model.setName("B"); commit = getTestEKBCommit(); commit.addUpdate(model); ContextHolder.get().setCurrentContextId("A"); persist.commit(commit, query.getLastRevisionNumberOfContext("A")); ContextHolder.get().setCurrentContextId("B"); persist.commit(commit, query.getLastRevisionNumberOfContext("B")); ContextHolder.get().setCurrentContextId("A"); EDBObject obj = edbService.getObject(getModelOid(oid)); assertThat(obj, notNullValue()); assertThat(obj.getString("name"), is("B")); ContextHolder.get().setCurrentContextId("B"); obj = edbService.getObject(getModelOid(oid)); assertThat(obj, notNullValue()); assertThat(obj.getString("name"), is("B")); } class ModelCommitThread extends Thread { private PersistInterface persist; private EKBCommit commit; private String contextId; public ModelCommitThread(PersistInterface persist, EKBCommit commit, String contextId) { this.persist = persist; this.commit = commit; this.contextId = contextId; } @Override public void run() { ContextHolder.get().setCurrentContextId(contextId); persist.commit(commit); } } }