package com.sixsq.slipstream.statemachine; /* * +=================================================================+ * SlipStream Server (WAR) * ===== * Copyright (C) 2013 SixSq Sarl (sixsq.com) * ===== * 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. * -=================================================================- */ import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.restlet.Request; import org.restlet.Response; import org.restlet.representation.StringRepresentation; import com.sixsq.slipstream.exceptions.AbortException; import com.sixsq.slipstream.exceptions.ConfigurationException; import com.sixsq.slipstream.exceptions.NotFoundException; import com.sixsq.slipstream.exceptions.SlipStreamException; import com.sixsq.slipstream.exceptions.ValidationException; import com.sixsq.slipstream.factory.RunFactory; import com.sixsq.slipstream.persistence.DeploymentModule; import com.sixsq.slipstream.persistence.ImageModule; import com.sixsq.slipstream.persistence.Node; import com.sixsq.slipstream.persistence.Package; import com.sixsq.slipstream.persistence.PersistenceUtil; import com.sixsq.slipstream.persistence.Run; import com.sixsq.slipstream.persistence.RunType; import com.sixsq.slipstream.persistence.RuntimeParameter; import com.sixsq.slipstream.run.RuntimeParameterResourceTestBase; import com.sixsq.slipstream.util.CommonTestUtil; public class StateMachineMultiThreadingTest extends RuntimeParameterResourceTestBase { private static String ORCHESTRATOR_NAME = Run.ORCHESTRATOR_NAME + "-local"; private static int errors = 0; public synchronized static void incrementError() { errors++; } @BeforeClass public static void classSetup() throws ValidationException { RuntimeParameterResourceTestBase.classSetup(); } @AfterClass public static void classTearDown() throws ValidationException { RuntimeParameterResourceTestBase.classTearDown(); } protected static String createParameterName(String key, String nodename) { return nodename + RuntimeParameter.NODE_PROPERTY_SEPARATOR + key; } protected static String createQualifiedNodeName(String nodename, int index) { return nodename + RuntimeParameter.NODE_MULTIPLICITY_INDEX_SEPARATOR + index; } public class ChangeStateRunnable implements Runnable { private String nodename; private Run run; public boolean success; public ChangeStateRunnable(Run run, String nodename) { this.nodename = nodename; this.run = run; } @Override public void run() { Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put("uuid", run.getUuid()); attributes.put("key", createParameterName(RuntimeParameter.COMPLETE_KEY)); Request request= null; try { request = createPostRequest(attributes, new StringRepresentation("")); } catch (ConfigurationException e) { fail(); } catch (ValidationException e) { fail(); } Response response = executeRequest(request); success = response.getStatus().isSuccess(); if (!success) { StateMachineMultiThreadingTest.incrementError(); } } private String createParameterName(String key) { return StateMachineMultiThreadingTest.createParameterName(key, nodename); } } private static final int MULTIPLICITY = 50; private static final String NODE_NAME = "node1"; private static final boolean MULTI_THREADED = true; private States initialState = States.Executing; private States finalState = States.SendingReports; @Test public void testMultipleStateTransitions() throws FileNotFoundException, IOException, SlipStreamException, InterruptedException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException { errors = 0; CommonTestUtil .resetAndLoadConnector(com.sixsq.slipstream.connector.local.LocalConnector.class); EntityManager em = PersistenceUtil.createEntityManager(); Run run = initRun(em); em.close(); List<Thread> threads = execute(run, MULTI_THREADED); waitAllThreadsComplete(threads); try { assertTransitionReached(Run.load(run.getResourceUri()), finalState); assertThat(errors, is(0)); } finally { run.remove(); } } private Run initRun(EntityManager em) throws FileNotFoundException, IOException, SlipStreamException { EntityTransaction transaction = em.getTransaction(); transaction.begin(); Run run = createRunDeployment("testMultipleStateTransitions"); run.getRuntimeParameters().get(RuntimeParameter.GLOBAL_STATE_KEY) .setValue(initialState.name()); run = em.merge(run); transaction.commit(); return run; } private List<Thread> execute(Run run, boolean multithreaded) { List<Thread> threads = new ArrayList<Thread>(); if (multithreaded) { for (int i = 1; i <= MULTIPLICITY; i++) { Thread thread = new Thread(new ChangeStateRunnable(run, createQualifiedNodeName(NODE_NAME, i))); thread.start(); threads.add(thread); } Thread thread = new Thread(new ChangeStateRunnable(run, ORCHESTRATOR_NAME)); thread.start(); threads.add(thread); } else { ChangeStateRunnable r; for (int i = 1; i <= MULTIPLICITY; i++) { r = new ChangeStateRunnable(run, createQualifiedNodeName( NODE_NAME, i)); r.run(); } r = new ChangeStateRunnable(run, ORCHESTRATOR_NAME); r.run(); } return threads; } private void waitAllThreadsComplete(List<Thread> threads) throws InterruptedException { for (Thread thread : threads) { thread.join(); } } private Run createRunDeployment(String moduleName) throws FileNotFoundException, IOException, SlipStreamException { image = new ImageModule(moduleName); image.setModuleReference(baseImage); image.getPackages().add(new Package("package1")); image.setModuleReference(baseImage); image.setImageId("123", cloudServiceName); Node node = new Node(NODE_NAME, image); node.setMultiplicity(MULTIPLICITY); node.setCloudService(cloudServiceName); deployment = new DeploymentModule("createAndStoreRunDeployment"); deployment.setNode(node); deployment.store(); Run run = RunFactory.getRun(deployment, RunType.Orchestration, user); return run; } private void assertTransitionReached(Run run, States expectedState) throws NotFoundException, AbortException { EntityManager em = PersistenceUtil.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); run = em.find(Run.class, run.getResourceUri()); String actualState = run .getRuntimeParameterValue(RuntimeParameter.GLOBAL_STATE_KEY); for (int i = 1; i <= MULTIPLICITY; i++) { String key = createParameterName(RuntimeParameter.COMPLETE_KEY, createQualifiedNodeName(NODE_NAME, i)); String complete = run.getRuntimeParameterValue(key); assertThat(complete, is("false")); } String key = createParameterName(RuntimeParameter.COMPLETE_KEY, ORCHESTRATOR_NAME); String complete = run.getRuntimeParameterValue(key); System.out.println("Node: " + ORCHESTRATOR_NAME + " is " + complete); assertThat(States.valueOf(actualState), is(expectedState)); transaction.commit(); em.close(); } }