From 7d88eb4f1f9e25ddc884f87385e417f428d701c3 Mon Sep 17 00:00:00 2001 From: Mark Little Date: Fri, 25 Dec 2015 17:46:44 +0000 Subject: [PATCH] Moved mod-stm here https://github.com/nmcl/TransactionalVert.x/issues/1 --- vertx/module/README.md | 36 ++ vertx/module/pom.xml | 340 ++++++++++++++++++ vertx/module/src/main/README.txt | 6 + vertx/module/src/main/assembly/mod.xml | 22 ++ .../main/java/org/jboss/stm/STMVerticle.java | 142 ++++++++ vertx/module/src/main/platform_lib/README.txt | 3 + .../src/main/resources/jbossts-properties.xml | 4 + vertx/module/src/main/resources/mod.json | 10 + .../java/ModuleIntegrationTest.java | 86 +++++ .../src/test/java/unit/BasicUnitTest.java | 154 ++++++++ vertx/module/src/test/java/unit/README.md | 5 + .../src/test/java/unit/STMUnitTest.java | 46 +++ .../src/test/java/unit/SampleUnitTest.java | 110 ++++++ vertx/module/vertx_classpath.txt | 11 + 14 files changed, 975 insertions(+) create mode 100644 vertx/module/README.md create mode 100644 vertx/module/pom.xml create mode 100644 vertx/module/src/main/README.txt create mode 100644 vertx/module/src/main/assembly/mod.xml create mode 100644 vertx/module/src/main/java/org/jboss/stm/STMVerticle.java create mode 100644 vertx/module/src/main/platform_lib/README.txt create mode 100644 vertx/module/src/main/resources/jbossts-properties.xml create mode 100644 vertx/module/src/main/resources/mod.json create mode 100644 vertx/module/src/test/java/integration/java/ModuleIntegrationTest.java create mode 100644 vertx/module/src/test/java/unit/BasicUnitTest.java create mode 100644 vertx/module/src/test/java/unit/README.md create mode 100644 vertx/module/src/test/java/unit/STMUnitTest.java create mode 100644 vertx/module/src/test/java/unit/SampleUnitTest.java create mode 100644 vertx/module/vertx_classpath.txt diff --git a/vertx/module/README.md b/vertx/module/README.md new file mode 100644 index 0000000..2ac6a4e --- /dev/null +++ b/vertx/module/README.md @@ -0,0 +1,36 @@ +# Vert.x STM Module + +A module that exposes the Narayana STM implementation for Vert.x applications. + +Documentation is a bit light at the moment, but the following links provide some background. + +http://jbossts.blogspot.co.uk/2011/06/stm-arjuna.html + +http://jbossts.blogspot.co.uk/2012/02/optimistic-stm.html + +http://jbossts.blogspot.co.uk/2012/03/update-to-stm-api.html + +http://jbossts.blogspot.co.uk/2013/03/stm-vertx-and-pi-part-1.html + +In a nutshell: + +(i) create an interface that defines the type(s) of your STM implementations. Annotate this using +@Transactional You can also use the @Optimistic annotation to choose to have optimistic concurrency +control for instances of the classes derived from the interface. (Default value is @Pessimistic). + +(ii) instrument the interface methods with @WriteLock or @ReadLock. (Default value is @WriteLock). + +(iii) define class(es) derives from the interface. Use the @NotState to select variables which are not +to be serialised as part of the transactional updates. (Default is to serialise everything). You can use +the @State annotation to make it explicit. + +(iv) create a Container that can manage instances of the interface defined in step (i). This is your +"transactional memory pool". Create instances of the classes from step (iii) and pass them to the Container. + +(v) manipulate the objects returned from the Container in step (iv) within the scope of AtomicActions. + +Note, this is NOT distributed transactions. No transaction context will flow between address spaces. +No interposition will occur if you manage to serialise and distribute a transaction context. Distributed +transactions could be added, but since 2PC is a blocking protocol it doesn't necessarily fit naturally +with Vert.x. Perhaps something based on forward compensations, which are not blocking. However, that then +introduces a trade-off between consistency and performance. Definitely a TODO. \ No newline at end of file diff --git a/vertx/module/pom.xml b/vertx/module/pom.xml new file mode 100644 index 0000000..44f0d48 --- /dev/null +++ b/vertx/module/pom.xml @@ -0,0 +1,340 @@ + + + 4.0.0 + + org.jboss + mod-stm + jar + 1.0-SNAPSHOT + Project - mod-stm + http://maven.apache.org + + + org.sonatype.oss + oss-parent + 7 + + + + UTF-8 + + + false + + + false + + + ${project.groupId}~${project.artifactId}~${project.version} + + + target/mods + + + 2.1M3 + 2.0.3-final + 4.11 + + + 3.0 + 2.6 + 2.5 + 2.0.3-final + 2.14 + 2.14 + 2.14 + 2.9 + 2.7 + + + + + sonatype-nexus-snapshots + https://oss.sonatype.org/content/repositories/snapshots + + + + + + + io.vertx + vertx-core + ${vertx.version} + provided + + + io.vertx + vertx-platform + ${vertx.version} + provided + + + io.vertx + vertx-hazelcast + ${vertx.version} + provided + + + + junit + junit + 4.11 + test + + + io.vertx + testtools + ${vertx.testtools.version} + test + + + + org.jboss.narayana.arjunacore + arjunacore + 5.0.1.Final + compile + + + org.jboss.narayana.arjunacore + txoj + 5.0.1.Final + compile + + + org.jboss.logging + jboss-logging + 3.1.4.GA + compile + + + org.jboss.narayana.stm + stm + 5.0.1.Final + compile + + + + + + + + + + + + io.vertx + vertx-maven-plugin + ${maven.vertx.plugin.version} + + + + + io.vertx + vertx-platform + ${vertx.version} + + + io.vertx + vertx-core + ${vertx.version} + + + io.vertx + vertx-hazelcast + ${vertx.version} + + + + + PullInDeps + prepare-package + + pullInDeps + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + 1.7 + 1.7 + + + + maven-clean-plugin + + + + ObjectStore + + + + + + maven-resources-plugin + ${maven.resources.plugin.version} + + + copy-mod-to-target + process-classes + + copy-resources + + + true + ${mods.directory}/${module.name} + + + target/classes + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven.dependency.plugin.version} + + + copy-mod-dependencies-to-target + process-classes + + copy-dependencies + + + ${mods.directory}/${module.name}/lib + runtime + + + + copy-mod-dependencies-to-target-dependencies + process-classes + + copy-dependencies + + + target/dependencies + runtime + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + + **/unit/*Test*.java + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven.failsafe.plugin.version} + + + + vertx.mods + ${mods.directory} + + + + **/integration/** + + + + + + integration-test + verify + + + + + + org.apache.maven.plugins + maven-surefire-report-plugin + ${maven.surefire.report.plugin.version} + + + generate-test-report + test + + report-only + + + + generate-integration-test-report + integration-test + + failsafe-report-only + + + + + + maven-assembly-plugin + + + src/main/assembly/mod.xml + + + + + assemble + package + + single + + + + + + + + + + org.apache.maven.plugins + maven-surefire-report-plugin + ${maven.surefire.report.plugin.version} + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.plugin.version} + + true + + + + + diff --git a/vertx/module/src/main/README.txt b/vertx/module/src/main/README.txt new file mode 100644 index 0000000..0447763 --- /dev/null +++ b/vertx/module/src/main/README.txt @@ -0,0 +1,6 @@ +Put any Java or Groovy classes used in your module in the java or groovy directories. + +Put any other resources that you want included in your module in the resources directory, this includes any +JavaScript, Ruby, Python, Groovy or CoffeeScript scripts or any other stuff you want in your module. + +The mod.json file also goes in the resources directory so it's copied over too. \ No newline at end of file diff --git a/vertx/module/src/main/assembly/mod.xml b/vertx/module/src/main/assembly/mod.xml new file mode 100644 index 0000000..941934a --- /dev/null +++ b/vertx/module/src/main/assembly/mod.xml @@ -0,0 +1,22 @@ + + + + mod + + zip + + + false + + + + + ${mods.directory}/${module.name} + + ** + + + + diff --git a/vertx/module/src/main/java/org/jboss/stm/STMVerticle.java b/vertx/module/src/main/java/org/jboss/stm/STMVerticle.java new file mode 100644 index 0000000..fcccc3e --- /dev/null +++ b/vertx/module/src/main/java/org/jboss/stm/STMVerticle.java @@ -0,0 +1,142 @@ +package org.jboss.stm; + +/* + * Copyright 2011 the original author or authors. + * + * 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 org.vertx.java.core.Handler; +import org.vertx.java.platform.Verticle; +import org.vertx.java.core.eventbus.Message; + +import org.jboss.stm.annotations.Transactional; +import org.jboss.stm.annotations.ReadLock; +import org.jboss.stm.annotations.State; +import org.jboss.stm.annotations.WriteLock; +import org.jboss.stm.Container; + +import com.arjuna.ats.arjuna.AtomicAction; + +/** + * This verticle is pretty artificial and is here only so that the module + * has something that people can look at and hopefully understand where STM + * fits in. + * + * Could consider removing this and making the module a pure library version, with + * some tests/examples separate. + */ + +public class STMVerticle extends Verticle { + + public STMVerticle () + { + transactionalObject = theContainer.create(new SampleLockable(10)); + + System.out.println("Object name: "+theContainer.getIdentifier(transactionalObject)); + + AtomicAction A = new AtomicAction(); + + /* + * Flush state to disk (for this example). + */ + + A.begin(); + + transactionalObject.increment(); + + A.commit(); + } + + @Transactional + public interface Sample + { + public void increment (); + public void decrement (); + + public int value (); + } + + @Transactional + public class SampleLockable implements Sample + { + public SampleLockable (int init) + { + _isState = init; + } + + @ReadLock + public int value () + { + return _isState; + } + + @WriteLock + public void increment () + { + _isState++; + } + + @WriteLock + public void decrement () + { + _isState--; + } + + @State + private int _isState; + } + + static public int value () + { + AtomicAction A = new AtomicAction(); + int result = -1; + + A.begin(); + + transactionalObject.increment(); + + result = transactionalObject.value(); + + A.commit(); + + return result; + } + + public void start() { + + vertx.eventBus().registerHandler("ping-address", new Handler>() { + @Override + public void handle(Message message) { + + //Now send some data + for (int i = 0; i < 10; i++) { + + int value = STMVerticle.value(); + + message.reply("pong! "+value); + + container.logger().info("Sent back pong "+value); + } + } + }); + + container.logger().info("STMVerticle started"); + } + + /* + * Have a persistent container for this example, but it's likely recoverable and optimistic cc are better for Vert.x. + */ + static final private Container theContainer = new Container("Demo", Container.TYPE.PERSISTENT, Container.MODEL.SHARED); + static private Sample transactionalObject = null; +} diff --git a/vertx/module/src/main/platform_lib/README.txt b/vertx/module/src/main/platform_lib/README.txt new file mode 100644 index 0000000..5760834 --- /dev/null +++ b/vertx/module/src/main/platform_lib/README.txt @@ -0,0 +1,3 @@ +If you want override the default langs.properties, cluster.xml or any other config for the Vert.x platform (i.e. +not for the module!) then you can add them in here and they will be added to the platform classpath when running +your module using Gradle. \ No newline at end of file diff --git a/vertx/module/src/main/resources/jbossts-properties.xml b/vertx/module/src/main/resources/jbossts-properties.xml new file mode 100644 index 0000000..bce3ea2 --- /dev/null +++ b/vertx/module/src/main/resources/jbossts-properties.xml @@ -0,0 +1,4 @@ + + + 1 + diff --git a/vertx/module/src/main/resources/mod.json b/vertx/module/src/main/resources/mod.json new file mode 100644 index 0000000..9b96628 --- /dev/null +++ b/vertx/module/src/main/resources/mod.json @@ -0,0 +1,10 @@ +{ + // Java verticle + "main":"org.jboss.stm.STMVerticle", + + "description":"STM implementation module for Vert.x. Relies on JBoss Narayana project.", + "licenses": ["LGPL 2.1"], + "author": "nmcl", + "keywords": ["stm", "transactions", "narayana", "persistence", "actors"], + "homepage": "https://github.com/nmcl/mod-stm" +} \ No newline at end of file diff --git a/vertx/module/src/test/java/integration/java/ModuleIntegrationTest.java b/vertx/module/src/test/java/integration/java/ModuleIntegrationTest.java new file mode 100644 index 0000000..bb8cd34 --- /dev/null +++ b/vertx/module/src/test/java/integration/java/ModuleIntegrationTest.java @@ -0,0 +1,86 @@ +package integration.java; +/* + * Copyright 2013 Red Hat, Inc. + * + * Red Hat 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. + * + * @author Tim Fox + */ + +import org.junit.Test; +import org.vertx.java.core.AsyncResult; +import org.vertx.java.core.AsyncResultHandler; +import org.vertx.java.core.Handler; +import org.vertx.java.core.eventbus.Message; +import org.vertx.java.core.http.HttpClientResponse; +import org.vertx.java.core.http.HttpServerRequest; +import org.vertx.testtools.TestVerticle; +import org.vertx.testtools.VertxAssert; + +import static org.vertx.testtools.VertxAssert.*; + +/** + * Example Java integration test that deploys the module that this project builds. + * + * Quite often in integration tests you want to deploy the same module for all tests and you don't want tests + * to start before the module has been deployed. + * + * This test demonstrates how to do that. + */ +public class ModuleIntegrationTest extends TestVerticle { + + @Test + public void testPing() { + container.logger().info("in testPing()"); + vertx.eventBus().send("ping-address", "ping!", new Handler>() { + @Override + public void handle(Message reply) { + assertEquals("pong! 12", reply.body()); + + /* + If we get here, the test is complete + You must always call `testComplete()` at the end. Remember that testing is *asynchronous* so + we cannot assume the test is complete by the time the test method has finished executing like + in standard synchronous tests + */ + testComplete(); + } + }); + } + + @Test + public void testSomethingElse() { + // Whatever + testComplete(); + } + + + @Override + public void start() { + // Make sure we call initialize() - this sets up the assert stuff so assert functionality works correctly + initialize(); + // Deploy the module - the System property `vertx.modulename` will contain the name of the module so you + // don't have to hardecode it in your tests + container.deployModule(System.getProperty("vertx.modulename"), new AsyncResultHandler() { + @Override + public void handle(AsyncResult asyncResult) { + // Deployment is asynchronous and this this handler will be called when it's complete (or failed) + assertTrue(asyncResult.succeeded()); + assertNotNull("deploymentID should not be null", asyncResult.result()); + // If deployed correctly then start the tests! + startTests(); + } + }); + } + +} diff --git a/vertx/module/src/test/java/unit/BasicUnitTest.java b/vertx/module/src/test/java/unit/BasicUnitTest.java new file mode 100644 index 0000000..63a1252 --- /dev/null +++ b/vertx/module/src/test/java/unit/BasicUnitTest.java @@ -0,0 +1,154 @@ +package unit; + +import org.junit.Test; + +/* + * Copyright 2013 Red Hat, Inc. + * + * Red Hat 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. + * + */ + +import java.io.IOException; + +import org.jboss.stm.annotations.State; +import org.jboss.stm.annotations.Transactional; +import org.jboss.stm.annotations.ReadLock; +import org.jboss.stm.annotations.WriteLock; +import org.jboss.stm.Container; + +import com.arjuna.ats.arjuna.AtomicAction; + +import org.junit.Test; +import static org.vertx.testtools.VertxAssert.*; + +/** + * @author Mark Little + */ + +public class BasicUnitTest +{ + /** + * This is out Transactional interface. We'll use this as the type for a + * Container. + */ + + // default pessimistic + + @Transactional + public interface Atomic + { + public void change (int value) throws Exception; + + public void set (int value) throws Exception; + + public int get () throws Exception; + } + + @Transactional + public class ExampleSTM implements Atomic + { + /* + * Define read/write operations here. Can do them in the interface + * if you want. + */ + + @ReadLock + public int get () throws Exception + { + return state; + } + + @WriteLock + public void set (int value) throws Exception + { + state = value; + } + + @WriteLock + public void change (int value) throws Exception + { + state += value; + } + + /** + * This is the state that will be manipulated (saved and restored). + */ + + private int state; + } + + @Test + public void testExampleSTM () throws Exception + { + /* + * Create the container for the Transactional interface. + */ + Container theContainer = new Container(); + + /* + * Create the instance of the class. But this won't be an STM object yet, so don't + * manipulate it just yet. + */ + + ExampleSTM basic = new ExampleSTM(); + boolean success = true; + + /* + * This object will be the one we actually use. + */ + + Atomic obj = null; + + try + { + /* + * Pass the instance we created previously to the Container so it + * can then create an STM object which we then use to manipulate + * the first object in a transactional manner. + */ + + obj = theContainer.create(basic); + } + catch (final Throwable ex) + { + ex.printStackTrace(); + + success = false; + } + + assertTrue(success); + + // a transaction! + + AtomicAction a = new AtomicAction(); + + a.begin(); + + obj.set(1234); + + a.commit(); + + assertEquals(obj.get(), 1234); + + a = new AtomicAction(); + + a.begin(); + + obj.change(1); // the value at this stage will be 1235 + + a.abort(); + + assertEquals(obj.get(), 1234); // we aborted, so the value should be back to 1234 + } +} \ No newline at end of file diff --git a/vertx/module/src/test/java/unit/README.md b/vertx/module/src/test/java/unit/README.md new file mode 100644 index 0000000..6daab4d --- /dev/null +++ b/vertx/module/src/test/java/unit/README.md @@ -0,0 +1,5 @@ +This directory includes a few unit tests which can also help illustrate some of the STM concepts + +BasicUnitTest shows how to create an STM which is managed via pessimistic concurrency control. + +SampleUnitTest shows how to create an STM with is managed via optimistic concurrency control. All of the methods of the object are assumed to modify the state since we don't use annotations to specify otherwise. \ No newline at end of file diff --git a/vertx/module/src/test/java/unit/STMUnitTest.java b/vertx/module/src/test/java/unit/STMUnitTest.java new file mode 100644 index 0000000..b355a04 --- /dev/null +++ b/vertx/module/src/test/java/unit/STMUnitTest.java @@ -0,0 +1,46 @@ +package unit; + +import org.jboss.stm.STMVerticle; +import org.junit.Test; + +/* + * Copyright 2013 Red Hat, Inc. + * + * Red Hat 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. + * + */ + +import com.arjuna.ats.arjuna.AtomicAction; +import org.junit.Test; + +import static org.vertx.testtools.VertxAssert.*; + +public class STMUnitTest { + + @Test + public void testVerticle() { + STMVerticle vert = new STMVerticle(); + + // do something with verticle + + AtomicAction A = new AtomicAction(); + + A.begin(); + + int amount = vert.value(); + + A.abort(); + + assertTrue(vert.value() == amount); + } +} diff --git a/vertx/module/src/test/java/unit/SampleUnitTest.java b/vertx/module/src/test/java/unit/SampleUnitTest.java new file mode 100644 index 0000000..a744c61 --- /dev/null +++ b/vertx/module/src/test/java/unit/SampleUnitTest.java @@ -0,0 +1,110 @@ +package unit; + +import org.junit.Test; + +/* + * Copyright 2013 Red Hat, Inc. + * + * Red Hat 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. + * + */ + +import org.jboss.stm.Container; +import org.jboss.stm.annotations.Optimistic; +import org.jboss.stm.annotations.Transactional; + +import com.arjuna.ats.arjuna.AtomicAction; + +import org.junit.Test; +import static org.vertx.testtools.VertxAssert.*; + +/** + * @author Mark Little + */ + +public class SampleUnitTest +{ + @Transactional + @Optimistic + public interface Sample + { + public void increment (); + public void decrement (); + + public int value (); + } + + @Transactional + @Optimistic + public class SampleLockable implements Sample + { + public SampleLockable () + { + this(0); + } + + public SampleLockable (int init) + { + _isState = init; + } + + public int value () + { + return _isState; + } + + public void increment () + { + _isState++; + } + + public void decrement () + { + _isState--; + } + + private int _isState; + } + + @Test + public void test () + { + /* + * Commented out until we get a fix in Narayana. + */ + + /* + Container theContainer = new Container(); + Sample obj1 = theContainer.create(new SampleLockable(10)); + Sample obj2 = theContainer.clone(new SampleLockable(), obj1); // could we do this by inference (look at 2nd parameter) or by annotation? + + assertTrue(obj2 != null); + + AtomicAction act = new AtomicAction(); + + act.begin(); + + obj1.increment(); + + act.commit(); + + act = new AtomicAction(); + + act.begin(); + + assertEquals(obj2.value(), 11); + + act.commit(); + */ + } +} \ No newline at end of file diff --git a/vertx/module/vertx_classpath.txt b/vertx/module/vertx_classpath.txt new file mode 100644 index 0000000..b8806eb --- /dev/null +++ b/vertx/module/vertx_classpath.txt @@ -0,0 +1,11 @@ +# This file contains information on where to find the resources of your module during development +# This file is used when running your module as you develop - it tells Vert.x where to find +# the resources of your module + +# Feel free to edit it if you have a non standard project structure and put the resources of your +# module elsewhere + +src/main/resources +target/classes +target/dependencies +bin \ No newline at end of file