IGNITE-14434 Adds examples of using thin client with spring-tx-ext. (#50)
diff --git a/modules/spring-tx-ext/examples/main/java/org/apache/ignite/transactions/spring/examples/IgniteClientTransactionalService.java b/modules/spring-tx-ext/examples/main/java/org/apache/ignite/transactions/spring/examples/IgniteClientTransactionalService.java
new file mode 100644
index 0000000..1d37301
--- /dev/null
+++ b/modules/spring-tx-ext/examples/main/java/org/apache/ignite/transactions/spring/examples/IgniteClientTransactionalService.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.transactions.spring.examples;
+
+import java.io.Serializable;
+import java.util.Objects;
+import org.apache.ignite.client.ClientCache;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.springframework.transaction.annotation.Isolation.READ_COMMITTED;
+import static org.springframework.transaction.annotation.Isolation.REPEATABLE_READ;
+
+/**
+ * Represents Spring Service that uses Ignite thin client to access Ignite cluster and perform cache transactional
+ * operations.
+ */
+@Service
+public class IgniteClientTransactionalService {
+ /** Ignite cache representation that uses thin client to communicate with Ignite cluster. */
+ private ClientCache<String, Account> cache;
+
+ /**
+ * The emitters transfer the specified funds to the broker. When both funds are received, they are transferred to
+ * the recipient, excluding the fee which the broker keeps for himself. If an error occurs at any step of this
+ * operation, it is rolled back.
+ */
+ @Transactional(isolation = REPEATABLE_READ)
+ public void transferFundsWithBroker(
+ String firstEmitter,
+ String secondEmitter,
+ String recipient,
+ String broker,
+ int funds,
+ int fee
+ ) {
+ transferFunds(firstEmitter, broker, funds);
+
+ transferFunds(secondEmitter, broker, funds);
+
+ transferFunds(broker, recipient, funds * 2 - fee);
+ }
+
+ /** Transfers funds between two accounts that belong to users with the specified names. */
+ @Transactional(isolation = REPEATABLE_READ)
+ public void transferFunds(String emitter, String recipient, int funds) {
+ Account emitterAcc = cache.get(emitter);
+ Account recipientAcc = cache.get(recipient);
+
+ if (emitterAcc.balance < funds)
+ throw new RuntimeException("Insufficient funds in " + emitter + "'s account");
+
+ emitterAcc.balance -= funds;
+ recipientAcc.balance += funds;
+
+ saveAccount(emitterAcc);
+ saveAccount(recipientAcc);
+
+ System.out.println(">>> " + emitter + " transfers " + funds + " coins to " + recipient);
+ }
+
+ /** Gets current balance of the account with the specified name.*/
+ @Transactional(isolation = READ_COMMITTED)
+ public int getBalance(String login) {
+ return cache.get(login).balance;
+ }
+
+ /** Creates account with the specified user login and balance. */
+ public Account createAccount(String login, int balance) {
+ Account acc = new Account(login);
+
+ acc.balance = balance;
+
+ cache.put(login, acc);
+
+ return acc;
+ }
+
+ /** Sets an Ignite cache representation that uses thin client to communicate with the Ignite cluster. */
+ public void setCache(ClientCache<String, Account> cache) {
+ this.cache = cache;
+ }
+
+ /** Puts the specified account into the cache. */
+ private void saveAccount(Account acc) {
+ cache.put(acc.login, acc);
+ }
+
+ /** Represents the user account. */
+ private static class Account implements Serializable {
+ /** Account owner login. */
+ private final String login;
+
+ /** Balance. */
+ private int balance;
+
+ /** Creates an account with the specified owner name. */
+ public Account(String login) {
+ this.login = login;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object other) {
+ if (this == other)
+ return true;
+
+ if (other == null || getClass() != other.getClass())
+ return false;
+
+ Account acc = (Account)other;
+
+ return balance == acc.balance && Objects.equals(login, acc.login);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(login, balance);
+ }
+ }
+}
diff --git a/modules/spring-tx-ext/examples/main/java/org/apache/ignite/transactions/spring/examples/SpringTransactionExample.java b/modules/spring-tx-ext/examples/main/java/org/apache/ignite/transactions/spring/examples/SpringTransactionExample.java
new file mode 100644
index 0000000..e534334
--- /dev/null
+++ b/modules/spring-tx-ext/examples/main/java/org/apache/ignite/transactions/spring/examples/SpringTransactionExample.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.ignite.transactions.spring.examples;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.ClientCacheConfiguration;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.transactions.spring.IgniteClientSpringTransactionManager;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.configuration.ClientConnectorConfiguration.DFLT_PORT;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+
+/** Represents example of using Ignite Spring Transactions integration with the thin client. */
+public class SpringTransactionExample {
+ /** Ignite cache name. */
+ public static final String ACCOUNT_CACHE_NAME = "example-account-cache";
+
+ /** */
+ public static void main(String[] args) {
+ try (
+ Ignite ignored = Ignition.start(); // Starts an Ignite cluster consisting of one server node.
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()
+ ) {
+ ctx.register(SpringApplicationConfiguration.class);
+ ctx.refresh();
+
+ IgniteClientTransactionalService svc = ctx.getBean(IgniteClientTransactionalService.class);
+
+ svc.createAccount("Bob", 1000);
+ svc.createAccount("Alice", 100);
+ svc.createAccount("Eve", 0);
+ svc.createAccount("Dave", 0);
+
+ doFundTransferWithBroker(svc, "Bob", "Alice", "Eve", "Dave", 1000, 10);
+
+ doFundTransferWithBroker(svc, "Bob", "Alice", "Eve", "Dave", 100, 10);
+ }
+ }
+
+ /** Delegates funds transfer operation to {@link IgniteClientTransactionalService} and logs the result. */
+ private static void doFundTransferWithBroker(
+ IgniteClientTransactionalService svc,
+ String firstEmitter,
+ String secondEmitter,
+ String recipient,
+ String broker,
+ int cash,
+ int fee
+ ) {
+ System.out.println("+--------------Fund transfer operation--------------+");
+
+ try {
+ svc.transferFundsWithBroker(firstEmitter, secondEmitter, recipient, broker, cash, fee);
+
+ System.out.println(">>> Operation completed successfully");
+ }
+ catch (RuntimeException e) {
+ System.out.println(">>> Operation was rolled back [error = " + e.getMessage() + ']');
+ }
+
+ System.out.println("\n>>> Account statuses:");
+
+ System.out.println(">>> " + firstEmitter + " balance: " + svc.getBalance(firstEmitter));
+ System.out.println(">>> " + secondEmitter + " balance: " + svc.getBalance(secondEmitter));
+ System.out.println(">>> " + recipient + " balance: " + svc.getBalance(recipient));
+ System.out.println(">>> " + broker + " balance: " + svc.getBalance(broker));
+ System.out.println("+---------------------------------------------------+");
+ }
+
+ /** Spring application configuration. */
+ @Configuration
+ @EnableTransactionManagement
+ public static class SpringApplicationConfiguration {
+ /**
+ * Ignite thin client instance that will be used to both initialize
+ * {@link IgniteClientSpringTransactionManager} and perform transactional cache operations.
+ */
+ @Bean
+ public IgniteClient igniteClient() {
+ return Ignition.startClient(new ClientConfiguration().setAddresses("127.0.0.1:" + DFLT_PORT));
+ }
+
+ /** Ignite implementation of the Spring Transactions manager interface. */
+ @Bean
+ public IgniteClientSpringTransactionManager transactionManager(IgniteClient cli) {
+ IgniteClientSpringTransactionManager mgr = new IgniteClientSpringTransactionManager();
+
+ mgr.setClientInstance(cli);
+ mgr.setTransactionConcurrency(PESSIMISTIC);
+
+ return mgr;
+ }
+
+ /** Service instance that uses declarative transaction management when working with the Ignite cache. */
+ @Bean
+ public IgniteClientTransactionalService transactionalService(IgniteClient cli) {
+ IgniteClientTransactionalService svc = new IgniteClientTransactionalService();
+
+ svc.setCache(cli.getOrCreateCache(new ClientCacheConfiguration()
+ .setName(ACCOUNT_CACHE_NAME)
+ .setAtomicityMode(TRANSACTIONAL)));
+
+ return svc;
+ }
+ }
+}
diff --git a/modules/spring-tx-ext/pom.xml b/modules/spring-tx-ext/pom.xml
index 4fd5c1d..b43da49 100644
--- a/modules/spring-tx-ext/pom.xml
+++ b/modules/spring-tx-ext/pom.xml
@@ -95,4 +95,52 @@
</testResource>
</testResources>
</build>
+
+ <profiles>
+ <profile>
+ <id>examples</id>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>examples/main/java</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-spring</artifactId>
+ <version>${ignite.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-core</artifactId>
+ <version>${ignite.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
</project>