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>