http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/montecarlo/CreditRiskManager.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/montecarlo/CreditRiskManager.java b/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/montecarlo/CreditRiskManager.java new file mode 100644 index 0000000..5bc3b13 --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/montecarlo/CreditRiskManager.java @@ -0,0 +1,143 @@ +/* + * 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.examples.java7.computegrid.montecarlo; + +import java.util.*; + +/** + * This class abstracts out the calculation of risk for a credit portfolio. + */ +@SuppressWarnings({"FloatingPointEquality"}) +public class CreditRiskManager { + /** + * Default randomizer with normal distribution. + * Note that since every JVM on the cluster will have its own random + * generator (independently initialized) the Monte-Carlo simulation + * will be slightly skewed when performed on the ignite cluster due to skewed + * normal distribution of the sub-jobs comparing to execution on the + * local node only with single random generator. Real-life applications + * may want to provide its own implementation of distributed random + * generator. + */ + private static Random rndGen = new Random(); + + /** + * Calculates credit risk for a given credit portfolio. This calculation uses + * Monte-Carlo Simulation to produce risk value. + * + * @param portfolio Credit portfolio. + * @param horizon Forecast horizon (in days). + * @param num Number of Monte-Carlo iterations. + * @param percentile Cutoff level. + * @return Credit risk value, i.e. the minimal amount that creditor has to + * have available to cover possible defaults. + */ + public double calculateCreditRiskMonteCarlo(Credit[] portfolio, int horizon, int num, double percentile) { + System.out.println(">>> Calculating credit risk for portfolio [size=" + portfolio.length + ", horizon=" + + horizon + ", percentile=" + percentile + ", iterations=" + num + "] <<<"); + + long start = System.currentTimeMillis(); + + double[] losses = calculateLosses(portfolio, horizon, num); + + Arrays.sort(losses); + + double[] lossProbs = new double[losses.length]; + + // Count variational numbers. + // Every next one either has the same value or previous one plus probability of loss. + for (int i = 0; i < losses.length; i++) + if (i == 0) + // First time it's just a probability of first value. + lossProbs[i] = getLossProbability(losses, 0); + else if (losses[i] != losses[i - 1]) + // Probability of this loss plus previous one. + lossProbs[i] = getLossProbability(losses, i) + lossProbs[i - 1]; + else + // The same loss the same probability. + lossProbs[i] = lossProbs[i - 1]; + + // Count percentile. + double crdRisk = 0; + + for (int i = 0; i < lossProbs.length; i++) + if (lossProbs[i] > percentile) { + crdRisk = losses[i - 1]; + + break; + } + + System.out.println(">>> Finished calculating portfolio risk [risk=" + crdRisk + + ", time=" + (System.currentTimeMillis() - start) + "ms]"); + + return crdRisk; + } + + /** + * Calculates losses for the given credit portfolio using Monte-Carlo Simulation. + * Simulates probability of default only. + * + * @param portfolio Credit portfolio. + * @param horizon Forecast horizon. + * @param num Number of Monte-Carlo iterations. + * @return Losses array simulated by Monte Carlo method. + */ + private double[] calculateLosses(Credit[] portfolio, int horizon, int num) { + double[] losses = new double[num]; + + // Count losses using Monte-Carlo method. We generate random probability of default, + // if it exceeds certain credit default value we count losses - otherwise count income. + for (int i = 0; i < num; i++) + for (Credit crd : portfolio) { + int remDays = Math.min(crd.getRemainingTerm(), horizon); + + if (rndGen.nextDouble() >= 1 - crd.getDefaultProbability(remDays)) + // (1 + 'r' * min(H, W) / 365) * S. + // Where W is a horizon, H is a remaining crediting term, 'r' is an annual credit rate, + // S is a remaining credit amount. + losses[i] += (1 + crd.getAnnualRate() * Math.min(horizon, crd.getRemainingTerm()) / 365) + * crd.getRemainingAmount(); + else + // - 'r' * min(H,W) / 365 * S + // Where W is a horizon, H is a remaining crediting term, 'r' is a annual credit rate, + // S is a remaining credit amount. + losses[i] -= crd.getAnnualRate() * Math.min(horizon, crd.getRemainingTerm()) / 365 * + crd.getRemainingAmount(); + } + + return losses; + } + + /** + * Calculates probability of certain loss in array of losses. + * + * @param losses Array of losses. + * @param i Index of certain loss in array. + * @return Probability of loss with given index. + */ + private double getLossProbability(double[] losses, int i) { + double cnt = 0; + double loss = losses[i]; + + for (double tmp : losses) + if (loss == tmp) + cnt++; + + return cnt / losses.length; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/montecarlo/package-info.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/montecarlo/package-info.java b/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/montecarlo/package-info.java new file mode 100644 index 0000000..f74ed7c --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/montecarlo/package-info.java @@ -0,0 +1,22 @@ +/* + * 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 description. --> + * Monte-Carlo simulation example. + */ +package org.apache.ignite.examples.java7.computegrid.montecarlo; http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/package-info.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/package-info.java b/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/package-info.java new file mode 100644 index 0000000..fe64345 --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/computegrid/package-info.java @@ -0,0 +1,22 @@ +/* + * 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 description. --> + * Basic examples for computational ignite functionality. + */ +package org.apache.ignite.examples.java7.computegrid; http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheAffinityExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheAffinityExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheAffinityExample.java new file mode 100644 index 0000000..0c387ab --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheAffinityExample.java @@ -0,0 +1,138 @@ +/* + * 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.examples.java7.datagrid; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.cluster.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.examples.java7.*; +import org.apache.ignite.lang.*; + +import java.util.*; + +/** + * This example demonstrates the simplest code that populates the distributed cache + * and co-locates simple closure execution with each key. The goal of this particular + * example is to provide the simplest code example of this logic. + * <p> + * Remote nodes should always be started with special configuration file which + * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}. + * <p> + * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will + * start node with {@code examples/config/example-ignite.xml} configuration. + */ +public final class CacheAffinityExample { + /** Cache name. */ + private static final String CACHE_NAME = CacheAffinityExample.class.getSimpleName(); + + /** Number of keys. */ + private static final int KEY_CNT = 20; + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws IgniteException If example execution failed. + */ + public static void main(String[] args) throws IgniteException { + try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { + System.out.println(); + System.out.println(">>> Cache affinity example started."); + + CacheConfiguration<Integer, String> cfg = new CacheConfiguration<>(); + + cfg.setCacheMode(CacheMode.PARTITIONED); + cfg.setName(CACHE_NAME); + + try (IgniteCache<Integer, String> cache = ignite.createCache(cfg)) { + for (int i = 0; i < KEY_CNT; i++) + cache.put(i, Integer.toString(i)); + + // Co-locates jobs with data using IgniteCompute.affinityRun(...) method. + visitUsingAffinityRun(); + + // Co-locates jobs with data using IgniteCluster.mapKeysToNodes(...) method. + visitUsingMapKeysToNodes(); + } + } + } + + /** + * Collocates jobs with keys they need to work on using + * {@link IgniteCompute#affinityRun(String, Object, IgniteRunnable)} method. + */ + private static void visitUsingAffinityRun() { + Ignite ignite = Ignition.ignite(); + + final IgniteCache<Integer, String> cache = ignite.jcache(CACHE_NAME); + + for (int i = 0; i < KEY_CNT; i++) { + final int key = i; + + // This runnable will execute on the remote node where + // data with the given key is located. Since it will be co-located + // we can use local 'peek' operation safely. + ignite.compute().affinityRun(CACHE_NAME, key, new IgniteRunnable() { + @Override public void run() { + // Peek is a local memory lookup, however, value should never be 'null' + // as we are co-located with node that has a given key. + System.out.println("Co-located using affinityRun [key= " + key + ", value=" + cache.localPeek(key) + ']'); + } + }); + } + } + + /** + * Collocates jobs with keys they need to work on using {@link IgniteCluster#mapKeysToNodes(String, Collection)} + * method. The difference from {@code affinityRun(...)} method is that here we process multiple keys + * in a single job. + */ + private static void visitUsingMapKeysToNodes() { + final Ignite ignite = Ignition.ignite(); + + Collection<Integer> keys = new ArrayList<>(KEY_CNT); + + for (int i = 0; i < KEY_CNT; i++) + keys.add(i); + + // Map all keys to nodes. + Map<ClusterNode, Collection<Integer>> mappings = ignite.cluster().mapKeysToNodes(CACHE_NAME, keys); + + for (Map.Entry<ClusterNode, Collection<Integer>> mapping : mappings.entrySet()) { + ClusterNode node = mapping.getKey(); + + final Collection<Integer> mappedKeys = mapping.getValue(); + + if (node != null) { + // Bring computations to the nodes where the data resides (i.e. collocation). + ignite.compute(ignite.cluster().forNode(node)).run(new IgniteRunnable() { + @Override public void run() { + IgniteCache<Integer, String> cache = ignite.jcache(CACHE_NAME); + + // Peek is a local memory lookup, however, value should never be 'null' + // as we are co-located with node that has a given key. + for (Integer key : mappedKeys) + System.out.println("Co-located using mapKeysToNodes [key= " + key + + ", value=" + cache.localPeek(key) + ']'); + } + }); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheApiExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheApiExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheApiExample.java new file mode 100644 index 0000000..0eaf2bd --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheApiExample.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.examples.java7.datagrid; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.examples.java7.*; +import org.apache.ignite.lang.*; + +import javax.cache.processor.*; +import java.util.concurrent.*; + +/** + * This example demonstrates some of the cache rich API capabilities. + * <p> + * Remote nodes should always be started with special configuration file which + * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}. + * <p> + * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will + * start node with {@code examples/config/example-ignite.xml} configuration. + */ +public class CacheApiExample { + /** Cache name. */ + private static final String CACHE_NAME = CacheApiExample.class.getSimpleName(); + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws IgniteException If example execution failed. + */ + public static void main(String[] args) throws IgniteException { + try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { + System.out.println(); + System.out.println(">>> Cache API example started."); + + CacheConfiguration<Integer, String> cfg = new CacheConfiguration<>(); + + cfg.setCacheMode(CacheMode.PARTITIONED); + cfg.setName(CACHE_NAME); + + try (IgniteCache<Integer, String> cache = ignite.createCache(cfg)) { + // Demonstrate atomic map operations. + atomicMapOperations(cache); + } + } + } + + /** + * Demonstrates cache operations similar to {@link ConcurrentMap} API. Note that + * cache API is a lot richer than the JDK {@link ConcurrentMap}. + * + * @throws IgniteException If failed. + */ + private static void atomicMapOperations(final IgniteCache<Integer, String> cache) throws IgniteException { + System.out.println(); + System.out.println(">>> Cache atomic map operation examples."); + + // Put and return previous value. + String v = cache.getAndPut(1, "1"); + assert v == null; + + // Put and do not return previous value (all methods ending with 'x' return boolean). + // Performs better when previous value is not needed. + cache.put(2, "2"); + + // Put asynchronously. + final IgniteCache<Integer, String> asyncCache = cache.withAsync(); + + asyncCache.put(3, "3"); + + asyncCache.get(3); + + IgniteFuture<String> fut = asyncCache.future(); + + //Asynchronously wait for result. + fut.listen(new IgniteInClosure<IgniteFuture<String>>() { + @Override + public void apply(IgniteFuture<String> fut) { + try { + System.out.println("Put operation completed [previous-value=" + fut.get() + ']'); + } + catch (IgniteException e) { + e.printStackTrace(); + } + } + }); + + // Put-if-absent. + boolean b1 = cache.putIfAbsent(4, "4"); + boolean b2 = cache.putIfAbsent(4, "44"); + assert b1 && !b2; + + // Invoke - assign new value based on previous value. + cache.put(6, "6"); + cache.invoke(6, new EntryProcessor<Integer, String, Object>() { + @Override public Object process(MutableEntry<Integer, String> entry, Object... args) { + String v = entry.getValue(); + + entry.setValue(v + "6"); // Set new value based on previous value. + + return null; + } + }); + + // Replace. + cache.put(7, "7"); + b1 = cache.replace(7, "7", "77"); + b2 = cache.replace(7, "7", "777"); + assert b1 & !b2; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheContinuousQueryExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheContinuousQueryExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheContinuousQueryExample.java new file mode 100644 index 0000000..88876df --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheContinuousQueryExample.java @@ -0,0 +1,107 @@ +/* + * 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.examples.java7.datagrid; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.cache.query.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.examples.java7.*; +import org.apache.ignite.lang.*; + +import javax.cache.*; +import javax.cache.event.*; + +/** + * This examples demonstrates continuous query API. + * <p> + * Remote nodes should always be started with special configuration file which + * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}. + * <p> + * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will + * start node with {@code examples/config/example-ignite.xml} configuration. + */ +public class CacheContinuousQueryExample { + /** Cache name. */ + private static final String CACHE_NAME = CacheContinuousQueryExample.class.getSimpleName(); + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws Exception If example execution failed. + */ + public static void main(String[] args) throws Exception { + try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { + System.out.println(); + System.out.println(">>> Cache continuous query example started."); + + CacheConfiguration<Integer, String> cfg = new CacheConfiguration<>(); + + cfg.setCacheMode(CacheMode.PARTITIONED); + cfg.setName(CACHE_NAME); + + try (IgniteCache<Integer, String> cache = ignite.createCache(cfg)) { + int keyCnt = 20; + + // These entries will be queried by initial predicate. + for (int i = 0; i < keyCnt; i++) + cache.put(i, Integer.toString(i)); + + // Create new continuous query. + ContinuousQuery<Integer, String> qry = new ContinuousQuery<>(); + + qry.setInitialQuery(new ScanQuery<>(new IgniteBiPredicate<Integer, String>() { + @Override public boolean apply(Integer key, String val) { + return key > 10; + } + })); + + // Callback that is called locally when update notifications are received. + qry.setLocalListener(new CacheEntryUpdatedListener<Integer, String>() { + @Override public void onUpdated(Iterable<CacheEntryEvent<? extends Integer, ? extends String>> evts) { + for (CacheEntryEvent<? extends Integer, ? extends String> e : evts) + System.out.println("Updated entry [key=" + e.getKey() + ", val=" + e.getValue() + ']'); + } + }); + + // This filter will be evaluated remotely on all nodes. + // Entry that pass this filter will be sent to the caller. + qry.setRemoteFilter(new IgniteCacheEntryEventFilter<Integer, String>() { + @Override public boolean evaluate(CacheEntryEvent<? extends Integer, ? extends String> e) { + return e.getKey() > 10; + } + }); + + // Execute query. + try (QueryCursor<Cache.Entry<Integer, String>> cur = cache.query(qry)) { + // Iterate through existing data. + for (Cache.Entry<Integer, String> e : cur) + System.out.println("Queried existing entry [key=" + e.getKey() + ", val=" + e.getValue() + ']'); + + // Add a few more keys and watch more query notifications. + for (int i = keyCnt; i < keyCnt + 10; i++) + cache.put(i, Integer.toString(i)); + + // Wait for a while while callback is notified about remaining puts. + Thread.sleep(2000); + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheDataStreamerExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheDataStreamerExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheDataStreamerExample.java new file mode 100644 index 0000000..fe788e2 --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheDataStreamerExample.java @@ -0,0 +1,91 @@ +/* + * 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.examples.java7.datagrid; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.examples.java7.*; + +/** + * Demonstrates how cache can be populated with data utilizing {@link IgniteDataStreamer} API. + * {@link IgniteDataStreamer} is a lot more efficient to use than standard + * {@code put(...)} operation as it properly buffers cache requests + * together and properly manages load on remote nodes. + * <p> + * Remote nodes should always be started with special configuration file which + * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}. + * <p> + * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will + * start node with {@code examples/config/example-ignite.xml} configuration. + */ +public class CacheDataStreamerExample { + /** Cache name. */ + private static final String CACHE_NAME = CacheDataStreamerExample.class.getSimpleName(); + + /** Number of entries to load. */ + private static final int ENTRY_COUNT = 500000; + + /** Heap size required to run this example. */ + public static final int MIN_MEMORY = 512 * 1024 * 1024; + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws IgniteException If example execution failed. + */ + public static void main(String[] args) throws IgniteException { + ExamplesUtils.checkMinMemory(MIN_MEMORY); + + try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { + System.out.println(); + System.out.println(">>> Cache data streamer example started."); + + CacheConfiguration<Integer, String> cfg = new CacheConfiguration<>(); + + cfg.setCacheMode(CacheMode.PARTITIONED); + cfg.setName(CACHE_NAME); + + try (IgniteCache<Integer, String> cache = ignite.createCache(cfg)) { + System.out.println(); + System.out.println(">>> Cache clear finished."); + + try (IgniteDataStreamer<Integer, String> ldr = ignite.dataStreamer(CACHE_NAME)) { + long start = System.currentTimeMillis(); + + // Configure loader. + ldr.perNodeBufferSize(1024); + ldr.perNodeParallelOperations(8); + + for (int i = 0; i < ENTRY_COUNT; i++) { + ldr.addData(i, Integer.toString(i)); + + // Print out progress while loading cache. + if (i > 0 && i % 10000 == 0) + System.out.println("Loaded " + i + " keys."); + } + + long end = System.currentTimeMillis(); + + System.out.println(">>> Loaded " + ENTRY_COUNT + " keys in " + (end - start) + "ms."); + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheEventsExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheEventsExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheEventsExample.java new file mode 100644 index 0000000..1aab270 --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheEventsExample.java @@ -0,0 +1,99 @@ +/* + * 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.examples.java7.datagrid; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.events.*; +import org.apache.ignite.examples.java7.*; +import org.apache.ignite.lang.*; + +import java.util.*; + +import static org.apache.ignite.events.EventType.*; + +/** + * This examples demonstrates events API. Note that ignite events are disabled by default and + * must be specifically enabled, just like in {@code examples/config/example-ignite.xml} file. + * <p> + * Remote nodes should always be started with special configuration file which + * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}. + * <p> + * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will + * start node with {@code examples/config/example-ignite.xml} configuration. + */ +public class CacheEventsExample { + /** Cache name. */ + private static final String CACHE_NAME = CacheEventsExample.class.getSimpleName(); + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws IgniteException If example execution failed. + */ + public static void main(String[] args) throws IgniteException, InterruptedException { + try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { + System.out.println(); + System.out.println(">>> Cache events example started."); + + CacheConfiguration<Integer, String> cfg = new CacheConfiguration<>(); + + cfg.setCacheMode(CacheMode.PARTITIONED); + cfg.setName(CACHE_NAME); + + try (IgniteCache<Integer, String> cache = ignite.createCache(cfg)) { + // This optional local callback is called for each event notification + // that passed remote predicate listener. + IgniteBiPredicate<UUID, CacheEvent> locLsnr = new IgniteBiPredicate<UUID, CacheEvent>() { + @Override public boolean apply(UUID uuid, CacheEvent evt) { + System.out.println("Received event [evt=" + evt.name() + ", key=" + evt.key() + + ", oldVal=" + evt.oldValue() + ", newVal=" + evt.newValue()); + + return true; // Continue listening. + } + }; + + // Remote listener which only accepts events for keys that are + // greater or equal than 10 and if event node is primary for this key. + IgnitePredicate<CacheEvent> rmtLsnr = new IgnitePredicate<CacheEvent>() { + @Override public boolean apply(CacheEvent evt) { + System.out.println("Cache event [name=" + evt.name() + ", key=" + evt.key() + ']'); + + int key = evt.key(); + + return key >= 10 && ignite.affinity(CACHE_NAME).isPrimary(ignite.cluster().localNode(), key); + } + }; + + // Subscribe to specified cache events on all nodes that have cache running. + // Cache events are explicitly enabled in examples/config/example-ignite.xml file. + ignite.events(ignite.cluster().forCacheNodes(CACHE_NAME)).remoteListen(locLsnr, rmtLsnr, + EVT_CACHE_OBJECT_PUT, EVT_CACHE_OBJECT_READ, EVT_CACHE_OBJECT_REMOVED); + + // Generate cache events. + for (int i = 0; i < 20; i++) + cache.put(i, Integer.toString(i)); + + // Wait for a while while callback is notified about remaining puts. + Thread.sleep(2000); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CachePopularNumbersExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CachePopularNumbersExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CachePopularNumbersExample.java new file mode 100644 index 0000000..db1cb31 --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CachePopularNumbersExample.java @@ -0,0 +1,172 @@ +/* + * 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.examples.java7.datagrid; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.cache.query.*; +import org.apache.ignite.cluster.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.examples.java7.*; +import org.apache.ignite.stream.*; + +import javax.cache.processor.*; +import java.util.*; + +/** + * Real time popular numbers counter. + * <p> + * Remote nodes should always be started with special configuration file which + * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}. + * <p> + * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will + * start node with {@code examples/config/example-ignite.xml} configuration. + */ +public class CachePopularNumbersExample { + /** Cache name. */ + private static final String CACHE_NAME = CachePopularNumbersExample.class.getSimpleName(); + + /** Count of most popular numbers to retrieve from cluster. */ + private static final int POPULAR_NUMBERS_CNT = 10; + + /** Random number generator. */ + private static final Random RAND = new Random(); + + /** Range within which to generate numbers. */ + private static final int RANGE = 1000; + + /** Count of total numbers to generate. */ + private static final int CNT = 1000000; + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws IgniteException If example execution failed. + */ + public static void main(String[] args) throws IgniteException { + Timer popularNumbersQryTimer = new Timer("numbers-query-worker"); + + try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { + System.out.println(); + System.out.println(">>> Cache popular numbers example started."); + + CacheConfiguration<Integer, Long> cfg = new CacheConfiguration<>(); + + cfg.setCacheMode(CacheMode.PARTITIONED); + cfg.setName(CACHE_NAME); + cfg.setIndexedTypes( + Integer.class, Long.class + ); + + try (IgniteCache<Integer, Long> cache = ignite.createCache(cfg)) { + ClusterGroup prj = ignite.cluster().forCacheNodes(CACHE_NAME); + + if (prj.nodes().isEmpty()) { + System.out.println("Ignite does not have cache configured: " + CACHE_NAME); + + return; + } + + TimerTask task = scheduleQuery(ignite, popularNumbersQryTimer, POPULAR_NUMBERS_CNT); + + streamData(ignite); + + // Force one more run to get final counts. + task.run(); + + popularNumbersQryTimer.cancel(); + } + } + } + + /** + * Populates cache in real time with numbers and keeps count for every number. + * + * @param ignite Ignite. + * @throws IgniteException If failed. + */ + private static void streamData(final Ignite ignite) throws IgniteException { + try (IgniteDataStreamer<Integer, Long> stmr = ignite.dataStreamer(CACHE_NAME)) { + // Set larger per-node buffer size since our state is relatively small. + stmr.perNodeBufferSize(2048); + + stmr.receiver(new IncrementingUpdater()); + + for (int i = 0; i < CNT; i++) + stmr.addData(RAND.nextInt(RANGE), 1L); + } + } + + /** + * Schedules our popular numbers query to run every 3 seconds. + * + * @param ignite Ignite. + * @param timer Timer. + * @param cnt Number of popular numbers to return. + * @return Scheduled task. + */ + private static TimerTask scheduleQuery(final Ignite ignite, Timer timer, final int cnt) { + TimerTask task = new TimerTask() { + @Override public void run() { + // Get reference to cache. + IgniteCache<Integer, Long> cache = ignite.jcache(CACHE_NAME); + + try { + List<List<?>> results = cache.query( + new SqlFieldsQuery("select _key, _val from Long order by _val desc, _key limit ?").setArgs(cnt)) + .getAll(); + + for (List<?> res : results) + System.out.println(res.get(0) + "=" + res.get(1)); + + System.out.println("----------------"); + } + catch (Exception e) { + e.printStackTrace(); + } + } + }; + + timer.schedule(task, 3000, 3000); + + return task; + } + + /** + * Increments value for key. + */ + private static class IncrementingUpdater implements StreamReceiver<Integer, Long> { + /** Process entries to increase value by entry key. */ + private static final EntryProcessor<Integer, Long, ?> INC = new EntryProcessor<Integer, Long, Object>() { + @Override public Object process(MutableEntry<Integer, Long> e, Object... args) { + Long val = e.getValue(); + + e.setValue(val == null ? 1L : val + 1); + + return null; + } + }; + + /** {@inheritDoc} */ + @Override public void receive(IgniteCache<Integer, Long> cache, Collection<Map.Entry<Integer, Long>> entries) { + for (Map.Entry<Integer, Long> entry : entries) + cache.invoke(entry.getKey(), INC); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CachePutGetExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CachePutGetExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CachePutGetExample.java new file mode 100644 index 0000000..9f4c6bf --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CachePutGetExample.java @@ -0,0 +1,113 @@ +/* + * 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.examples.java7.datagrid; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.examples.java7.*; + +import java.util.*; + +/** + * This example demonstrates very basic operations on cache, such as 'put' and 'get'. + * <p> + * Remote nodes should always be started with special configuration file which + * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}. + * <p> + * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will + * start node with {@code examples/config/example-ignite.xml} configuration. + */ +public class CachePutGetExample { + /** Cache name. */ + private static final String CACHE_NAME = CachePutGetExample.class.getSimpleName(); + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws IgniteException If example execution failed. + */ + public static void main(String[] args) throws IgniteException { + try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { + + CacheConfiguration<Integer, String> cfg = new CacheConfiguration<>(); + + cfg.setCacheMode(CacheMode.PARTITIONED); + cfg.setName(CACHE_NAME); + + try (IgniteCache<Integer, String> cache = ignite.createCache(cfg)) { + // Individual puts and gets. + putGet(cache); + + // Bulk puts and gets. + putAllGetAll(cache); + } + } + } + + /** + * Execute individual puts and gets. + * + * @throws IgniteException If failed. + */ + private static void putGet(IgniteCache<Integer, String> cache) throws IgniteException { + System.out.println(); + System.out.println(">>> Cache put-get example started."); + + final int keyCnt = 20; + + // Store keys in cache. + for (int i = 0; i < keyCnt; i++) + cache.put(i, Integer.toString(i)); + + System.out.println(">>> Stored values in cache."); + + for (int i = 0; i < keyCnt; i++) + System.out.println("Got [key=" + i + ", val=" + cache.get(i) + ']'); + } + + /** + * Execute bulk {@code putAll(...)} and {@code getAll(...)} operations. + * + * @throws IgniteException If failed. + */ + private static void putAllGetAll(IgniteCache<Integer, String> cache) throws IgniteException { + System.out.println(); + System.out.println(">>> Starting putAll-getAll example."); + + final int keyCnt = 20; + + // Create batch. + Map<Integer, String> batch = new HashMap<>(); + + for (int i = 0; i < keyCnt; i++) + batch.put(i, "bulk-" + Integer.toString(i)); + + // Bulk-store entries in cache. + cache.putAll(batch); + + System.out.println(">>> Bulk-stored values in cache."); + + // Bulk-get values from cache. + Map<Integer, String> vals = cache.getAll(batch.keySet()); + + for (Map.Entry<Integer, String> e : vals.entrySet()) + System.out.println("Got entry [key=" + e.getKey() + ", val=" + e.getValue() + ']'); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheQueryExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheQueryExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheQueryExample.java new file mode 100644 index 0000000..b533731 --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheQueryExample.java @@ -0,0 +1,407 @@ +/* + * 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.examples.java7.datagrid; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.cache.affinity.*; +import org.apache.ignite.cache.query.*; +import org.apache.ignite.cache.query.annotations.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.examples.java7.*; + +import javax.cache.*; +import java.io.*; +import java.util.*; + +/** + * Cache queries example. This example demonstrates SQL, TEXT, and FULL SCAN + * queries over cache. + * <p> + * Example also demonstrates usage of fields queries that return only required + * fields instead of whole key-value pairs. When fields queries are distributed + * across several nodes, they may not work as expected. Keep in mind following + * limitations (not applied if data is queried from one node only): + * <ul> + * <li> + * {@code Group by} and {@code sort by} statements are applied separately + * on each node, so result set will likely be incorrectly grouped or sorted + * after results from multiple remote nodes are grouped together. + * </li> + * <li> + * Aggregation functions like {@code sum}, {@code max}, {@code avg}, etc. + * are also applied on each node. Therefore you will get several results + * containing aggregated values, one for each node. + * </li> + * <li> + * Joins will work correctly only if joined objects are stored in + * collocated mode. Refer to {@link CacheAffinityKey} javadoc for more details. + * </li> + * <li> + * Note that if you created query on to replicated cache, all data will + * be queried only on one node, not depending on what caches participate in + * the query (some data from partitioned cache can be lost). And visa versa, + * if you created it on partitioned cache, data from replicated caches + * will be duplicated. + * </li> + * </ul> + * <p> + * Remote nodes should always be started with special configuration file which + * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}. + * <p> + * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will + * start node with {@code examples/config/example-ignite.xml} configuration. + */ +public class CacheQueryExample { + /** Cache name. */ + private static final String CACHE_NAME = CacheQueryExample.class.getSimpleName(); + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws Exception If example execution failed. + */ + public static void main(String[] args) throws Exception { + try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { + System.out.println(); + System.out.println(">>> Cache query example started."); + + CacheConfiguration<?, ?> cfg = new CacheConfiguration<>(); + + cfg.setCacheMode(CacheMode.PARTITIONED); + cfg.setName(CACHE_NAME); + cfg.setIndexedTypes( + UUID.class, Organization.class, + CacheAffinityKey.class, Person.class + ); + + try (IgniteCache<?, ?> cache = ignite.createCache(cfg)) { + // Populate cache. + initialize(); + + // Example for SQL-based querying employees based on salary ranges. + sqlQuery(); + + // Example for SQL-based querying employees for a given organization (includes SQL join). + sqlQueryWithJoin(); + + // Example for TEXT-based querying for a given string in peoples resumes. + textQuery(); + + // Example for SQL-based querying to calculate average salary among all employees within a company. + sqlQueryWithAggregation(); + + // Example for SQL-based fields queries that return only required + // fields instead of whole key-value pairs. + sqlFieldsQuery(); + + // Example for SQL-based fields queries that return only required + // fields instead of whole key-value pairs. + sqlFieldsQuery(); + + // Example for SQL-based fields queries that uses joins. + sqlFieldsQueryWithJoin(); + } + + print("Cache query example finished."); + } + } + + /** + * Example for SQL queries based on salary ranges. + */ + private static void sqlQuery() { + IgniteCache<CacheAffinityKey<UUID>, Person> cache = Ignition.ignite().jcache(CACHE_NAME); + + // SQL clause which selects salaries based on range. + String sql = "salary > ? and salary <= ?"; + + // Execute queries for salary ranges. + print("People with salaries between 0 and 1000: ", + cache.query(new SqlQuery<CacheAffinityKey<UUID>, Person>(Person.class, sql). + setArgs(0, 1000)).getAll()); + + print("People with salaries between 1000 and 2000: ", + cache.query(new SqlQuery<CacheAffinityKey<UUID>, Person>(Person.class, sql). + setArgs(1000, 2000)).getAll()); + + print("People with salaries greater than 2000: ", + cache.query(new SqlQuery<CacheAffinityKey<UUID>, Person>(Person.class, sql). + setArgs(2000, Integer.MAX_VALUE)).getAll()); + } + + /** + * Example for SQL queries based on all employees working for a specific organization. + */ + private static void sqlQueryWithJoin() { + IgniteCache<CacheAffinityKey<UUID>, Person> cache = Ignition.ignite().jcache(CACHE_NAME); + + // SQL clause query which joins on 2 types to select people for a specific organization. + String joinSql = + "from Person, Organization " + + "where Person.orgId = Organization.id " + + "and lower(Organization.name) = lower(?)"; + + // Execute queries for find employees for different organizations. + print("Following people are 'GridGain' employees: ", + cache.query(new SqlQuery<CacheAffinityKey<UUID>, Person>(Person.class, joinSql). + setArgs("GridGain")).getAll()); + print("Following people are 'Other' employees: ", + cache.query(new SqlQuery<CacheAffinityKey<UUID>, Person>(Person.class, joinSql). + setArgs("Other")).getAll()); + } + + /** + * Example for TEXT queries using LUCENE-based indexing of people's resumes. + */ + private static void textQuery() { + IgniteCache<CacheAffinityKey<UUID>, Person> cache = Ignition.ignite().jcache(CACHE_NAME); + + // Query for all people with "Master Degree" in their resumes. + QueryCursor<Cache.Entry<CacheAffinityKey<UUID>, Person>> masters = + cache.query(new TextQuery<CacheAffinityKey<UUID>, Person>(Person.class, "Master")); + + // Query for all people with "Bachelor Degree" in their resumes. + QueryCursor<Cache.Entry<CacheAffinityKey<UUID>, Person>> bachelors = + cache.query(new TextQuery<CacheAffinityKey<UUID>, Person>(Person.class, "Bachelor")); + + print("Following people have 'Master Degree' in their resumes: ", masters.getAll()); + print("Following people have 'Bachelor Degree' in their resumes: ", bachelors.getAll()); + } + + /** + * Example for SQL queries to calculate average salary for a specific organization. + */ + private static void sqlQueryWithAggregation() { + IgniteCache<CacheAffinityKey<UUID>, Person> cache = Ignition.ignite().jcache(CACHE_NAME); + + // Calculate average of salary of all persons in GridGain. + QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("select avg(salary) from Person, " + + "Organization where Person.orgId = Organization.id and " + "lower(Organization.name) = lower(?)" + ).setArgs("GridGain")); + + // Calculate average salary for a specific organization. + print("Average salary for 'GridGain' employees: " + cursor.getAll()); + } + + /** + * Example for SQL-based fields queries that return only required + * fields instead of whole key-value pairs. + */ + private static void sqlFieldsQuery() { + IgniteCache<?, ?> cache = Ignition.ignite().jcache(CACHE_NAME); + + // Create query to get names of all employees. + QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("select concat(firstName, ' ', " + + "lastName) from Person")); + + // Execute query to get collection of rows. In this particular + // case each row will have one element with full name of an employees. + List<List<?>> res = cursor.getAll(); + + // Print names. + print("Names of all employees:", res); + } + + /** + * Example for SQL-based fields queries that return only required + * fields instead of whole key-value pairs. + */ + private static void sqlFieldsQueryWithJoin() { + IgniteCache<?, ?> cache = Ignition.ignite().jcache(CACHE_NAME); + + // Execute query to get names of all employees. + QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("select concat(firstName, ' ', lastName), " + + "" + "Organization.name from Person, Organization where " + "Person.orgId = Organization.id")); + + // In this particular case each row will have one element with full name of an employees. + List<List<?>> res = cursor.getAll(); + + // Print persons' names and organizations' names. + print("Names of all employees and organizations they belong to:", res); + } + + /** + * Populate cache with test data. + */ + private static void initialize() { + IgniteCache<Object, Object> cache = Ignition.ignite().jcache(CACHE_NAME); + + // Organizations. + Organization org1 = new Organization("GridGain"); + Organization org2 = new Organization("Other"); + + // People. + Person p1 = new Person(org1, "John", "Doe", 2000, "John Doe has Master Degree."); + Person p2 = new Person(org1, "Jane", "Doe", 1000, "Jane Doe has Bachelor Degree."); + Person p3 = new Person(org2, "John", "Smith", 1000, "John Smith has Bachelor Degree."); + Person p4 = new Person(org2, "Jane", "Smith", 2000, "Jane Smith has Master Degree."); + + cache.put(org1.id, org1); + cache.put(org2.id, org2); + + // Note that in this example we use custom affinity key for Person objects + // to ensure that all persons are collocated with their organizations. + cache.put(p1.key(), p1); + cache.put(p2.key(), p2); + cache.put(p3.key(), p3); + cache.put(p4.key(), p4); + } + + /** + * Prints collection of objects to standard out. + * + * @param msg Message to print before all objects are printed. + * @param col Query results. + */ + private static void print(String msg, Iterable<?> col) { + if (msg != null) + System.out.println(">>> " + msg); + + print(col); + } + + /** + * Prints collection items. + * + * @param col Collection. + */ + private static void print(Iterable<?> col) { + for (Object next : col) { + if (next instanceof Iterable) + print((Iterable<?>)next); + else + System.out.println(">>> " + next); + } + } + + /** + * Prints out given object to standard out. + * + * @param o Object to print. + */ + private static void print(Object o) { + System.out.println(">>> " + o); + } + + /** + * Person class. + */ + private static class Person implements Serializable { + /** Person ID (indexed). */ + @QuerySqlField(index = true) + private UUID id; + + /** Organization ID (indexed). */ + @QuerySqlField(index = true) + private UUID orgId; + + /** First name (not-indexed). */ + @QuerySqlField + private String firstName; + + /** Last name (not indexed). */ + @QuerySqlField + private String lastName; + + /** Resume text (create LUCENE-based TEXT index for this field). */ + @QueryTextField + private String resume; + + /** Salary (indexed). */ + @QuerySqlField(index = true) + private double salary; + + /** Custom cache key to guarantee that person is always collocated with its organization. */ + private transient CacheAffinityKey<UUID> key; + + /** + * Constructs person record. + * + * @param org Organization. + * @param firstName First name. + * @param lastName Last name. + * @param salary Salary. + * @param resume Resume text. + */ + Person(Organization org, String firstName, String lastName, double salary, String resume) { + // Generate unique ID for this person. + id = UUID.randomUUID(); + + orgId = org.id; + + this.firstName = firstName; + this.lastName = lastName; + this.resume = resume; + this.salary = salary; + } + + /** + * Gets cache affinity key. Since in some examples person needs to be collocated with organization, we create + * custom affinity key to guarantee this collocation. + * + * @return Custom affinity key to guarantee that person is always collocated with organization. + */ + public CacheAffinityKey<UUID> key() { + if (key == null) + key = new CacheAffinityKey<>(id, orgId); + + return key; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "Person [firstName=" + firstName + + ", lastName=" + lastName + + ", id=" + id + + ", orgId=" + orgId + + ", resume=" + resume + + ", salary=" + salary + ']'; + } + } + + /** + * Organization class. + */ + private static class Organization implements Serializable { + /** Organization ID (indexed). */ + @QuerySqlField(index = true) + private UUID id; + + /** Organization name (indexed). */ + @QuerySqlField(index = true) + private String name; + + /** + * Create organization. + * + * @param name Organization name. + */ + Organization(String name) { + id = UUID.randomUUID(); + + this.name = name; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "Organization [id=" + id + ", name=" + name + ']'; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheTransactionExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheTransactionExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheTransactionExample.java new file mode 100644 index 0000000..970ab1f --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/CacheTransactionExample.java @@ -0,0 +1,148 @@ +/* + * 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.examples.java7.datagrid; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.examples.java7.*; +import org.apache.ignite.transactions.*; + +import java.io.*; + +import static org.apache.ignite.transactions.TransactionConcurrency.*; +import static org.apache.ignite.transactions.TransactionIsolation.*; + +/** + * Demonstrates how to use cache transactions. + * <p> + * Remote nodes should always be started with special configuration file which + * enables P2P class loading: {@code 'ignite.{sh|bat} examples/config/example-ignite.xml'}. + * <p> + * Alternatively you can run {@link ExampleNodeStartup} in another JVM which will + * start node with {@code examples/config/example-ignite.xml} configuration. + */ +public class CacheTransactionExample { + /** Cache name. */ + private static final String CACHE_NAME = CacheTransactionExample.class.getSimpleName(); + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws IgniteException If example execution failed. + */ + public static void main(String[] args) throws IgniteException { + try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) { + System.out.println(); + System.out.println(">>> Cache transaction example started."); + + CacheConfiguration<Integer, Account> cfg = new CacheConfiguration<>(); + + cfg.setCacheMode(CacheMode.PARTITIONED); + cfg.setName(CACHE_NAME); + cfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL); + + NearCacheConfiguration<Integer, Account> nearCacheCfg = new NearCacheConfiguration<>(); + + try (IgniteCache<Integer, Account> cache = ignite.createCache(cfg, nearCacheCfg)) { + // Initialize. + cache.put(1, new Account(1, 100)); + cache.put(2, new Account(1, 200)); + + System.out.println(); + System.out.println(">>> Accounts before deposit: "); + System.out.println(">>> " + cache.get(1)); + System.out.println(">>> " + cache.get(2)); + + // Make transactional deposits. + deposit(cache, 1, 100); + deposit(cache, 2, 200); + + System.out.println(); + System.out.println(">>> Accounts after transfer: "); + System.out.println(">>> " + cache.get(1)); + System.out.println(">>> " + cache.get(2)); + + System.out.println(">>> Cache transaction example finished."); + } + } + } + + /** + * Make deposit into specified account. + * + * @param acctId Account ID. + * @param amount Amount to deposit. + * @throws IgniteException If failed. + */ + private static void deposit(IgniteCache<Integer, Account> cache, int acctId, double amount) throws IgniteException { + try (Transaction tx = Ignition.ignite().transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) { + Account acct0 = cache.get(acctId); + + assert acct0 != null; + + Account acct = new Account(acct0.id, acct0.balance); + + // Deposit into account. + acct.update(amount); + + // Store updated account in cache. + cache.put(acctId, acct); + + tx.commit(); + } + + System.out.println(); + System.out.println(">>> Transferred amount: $" + amount); + } + + /** + * Account. + */ + private static class Account implements Serializable { + /** Account ID. */ + private int id; + + /** Account balance. */ + private double balance; + + /** + * @param id Account ID. + * @param balance Balance. + */ + Account(int id, double balance) { + this.id = id; + this.balance = balance; + } + + /** + * Change balance by specified amount. + * + * @param amount Amount to add to balance (may be negative). + */ + void update(double amount) { + balance += amount; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "Account [id=" + id + ", balance=$" + balance + ']'; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/HibernateL2CacheExample.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/HibernateL2CacheExample.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/HibernateL2CacheExample.java new file mode 100644 index 0000000..6eb6c72 --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/HibernateL2CacheExample.java @@ -0,0 +1,204 @@ +/* + * 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.examples.java7.datagrid.hibernate; + +import org.apache.ignite.*; +import org.apache.ignite.examples.java7.*; +import org.hibernate.*; +import org.hibernate.cache.spi.access.AccessType; +import org.hibernate.cfg.*; +import org.hibernate.service.*; +import org.hibernate.stat.*; + +import java.net.*; +import java.util.*; + +/** + * This example demonstrates the use of Ignite In-Memory Data Ignite cluster as a Hibernate + * Second-Level cache provider. + * <p> + * The Hibernate Second-Level cache (or "L2 cache" shortly) lets you significantly + * reduce the number of requests to the underlying SQL database. Because database + * access is known to be an expansive operation, using L2 cache may improve + * performance dramatically. + * <p> + * This example defines 2 entity classes: {@link User} and {@link Post}, with + * 1 <-> N relation, and marks them with appropriate annotations for Hibernate + * object-relational mapping to SQL tables of an underlying H2 in-memory database. + * The example launches node in the same JVM and registers it in + * Hibernate configuration as an L2 cache implementation. It then stores and + * queries instances of the entity classes to and from the database, having + * Hibernate SQL output, L2 cache statistics output, and Ignite cache metrics + * output enabled. + * <p> + * When running example, it's easy to notice that when an object is first + * put into a database, the L2 cache is not used and it's contents is empty. + * However, when an object is first read from the database, it is immediately + * stored in L2 cache (which is Ignite In-Memory Data Ignite cluster in fact), which can + * be seen in stats output. Further requests of the same object only read the data + * from L2 cache and do not hit the database. + * <p> + * In this example, the Hibernate query cache is also enabled. Query cache lets you + * avoid hitting the database in case of repetitive queries with the same parameter + * values. You may notice that when the example runs the same query repeatedly in + * loop, only the first query hits the database and the successive requests take the + * data from L2 cache. + * <p> + * Note: this example uses {@link AccessType#READ_ONLY} L2 cache access type, but you + * can experiment with other access types by modifying the Hibernate configuration file + * {@code IGNITE_HOME/examples/config/hibernate/example-hibernate-L2-cache.xml}, used by the example. + * <p> + * Remote nodes should always be started using {@link HibernateL2CacheExampleNodeStartup} + */ +public class HibernateL2CacheExample { + /** JDBC URL for backing database (an H2 in-memory database is used). */ + private static final String JDBC_URL = "jdbc:h2:mem:example;DB_CLOSE_DELAY=-1"; + + /** Path to hibernate configuration file (will be resolved from application {@code CLASSPATH}). */ + private static final String HIBERNATE_CFG = "hibernate/example-hibernate-L2-cache.xml"; + + /** Entity names for stats output. */ + private static final List<String> ENTITY_NAMES = + Arrays.asList(User.class.getName(), Post.class.getName(), User.class.getName() + ".posts"); + + /** + * Executes example. + * + * @param args Command line arguments, none required. + * @throws IgniteException If example execution failed. + */ + public static void main(String[] args) throws IgniteException { + // Start the node, run the example, and stop the node when finished. + try (Ignite ignite = Ignition.start(HibernateL2CacheExampleNodeStartup.configuration())) { + // We use a single session factory, but create a dedicated session + // for each transaction or query. This way we ensure that L1 cache + // is not used (L1 cache has per-session scope only). + System.out.println(); + System.out.println(">>> Hibernate L2 cache example started."); + + URL hibernateCfg = ExamplesUtils.url(HIBERNATE_CFG); + + SessionFactory sesFactory = createHibernateSessionFactory(hibernateCfg); + + System.out.println(); + System.out.println(">>> Creating objects."); + + final long userId; + + Session ses = sesFactory.openSession(); + + try { + Transaction tx = ses.beginTransaction(); + + User user = new User("jedi", "Luke", "Skywalker"); + + user.getPosts().add(new Post(user, "Let the Force be with you.")); + + ses.save(user); + + tx.commit(); + + // Create a user object, store it in DB, and save the database-generated + // object ID. You may try adding more objects in a similar way. + userId = user.getId(); + } + finally { + ses.close(); + } + + // Output L2 cache and Ignite cache stats. You may notice that + // at this point the object is not yet stored in L2 cache, because + // the read was not yet performed. + printStats(sesFactory); + + System.out.println(); + System.out.println(">>> Querying object by ID."); + + // Query user by ID several times. First time we get an L2 cache + // miss, and the data is queried from DB, but it is then stored + // in cache and successive queries hit the cache and return + // immediately, no SQL query is made. + for (int i = 0; i < 3; i++) { + ses = sesFactory.openSession(); + + try { + Transaction tx = ses.beginTransaction(); + + User user = (User)ses.get(User.class, userId); + + System.out.println("User: " + user); + + for (Post post : user.getPosts()) + System.out.println("\tPost: " + post); + + tx.commit(); + } + finally { + ses.close(); + } + } + + // Output the stats. We should see 1 miss and 2 hits for + // User and Collection object (stored separately in L2 cache). + // The Post is loaded with the collection, so it won't imply + // a miss. + printStats(sesFactory); + } + } + + /** + * Creates a new Hibernate {@link SessionFactory} using a programmatic + * configuration. + * + * @param hibernateCfg Hibernate configuration file. + * @return New Hibernate {@link SessionFactory}. + */ + private static SessionFactory createHibernateSessionFactory(URL hibernateCfg) { + ServiceRegistryBuilder builder = new ServiceRegistryBuilder(); + + builder.applySetting("hibernate.connection.url", JDBC_URL); + builder.applySetting("hibernate.show_sql", true); + + return new Configuration() + .configure(hibernateCfg) + .buildSessionFactory(builder.buildServiceRegistry()); + } + + /** + * Prints Hibernate L2 cache statistics to standard output. + * + * @param sesFactory Hibernate {@link SessionFactory}, for which to print + * statistics. + */ + private static void printStats(SessionFactory sesFactory) { + System.out.println("=== Hibernate L2 cache statistics ==="); + + for (String entityName : ENTITY_NAMES) { + System.out.println("\tEntity: " + entityName); + + SecondLevelCacheStatistics stats = + sesFactory.getStatistics().getSecondLevelCacheStatistics(entityName); + + System.out.println("\t\tL2 cache entries: " + stats.getEntries()); + System.out.println("\t\tHits: " + stats.getHitCount()); + System.out.println("\t\tMisses: " + stats.getMissCount()); + } + + System.out.println("====================================="); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/HibernateL2CacheExampleNodeStartup.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/HibernateL2CacheExampleNodeStartup.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/HibernateL2CacheExampleNodeStartup.java new file mode 100644 index 0000000..cf9b67c --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/HibernateL2CacheExampleNodeStartup.java @@ -0,0 +1,97 @@ +/* + * 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.examples.java7.datagrid.hibernate; + +import org.apache.ignite.*; +import org.apache.ignite.cache.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.spi.discovery.tcp.*; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.*; + +import java.util.*; + +import static org.apache.ignite.cache.CacheAtomicityMode.*; +import static org.apache.ignite.cache.CacheMode.*; +import static org.apache.ignite.cache.CacheWriteSynchronizationMode.*; + +/** + * Starts up an empty node with example cache configuration. + */ +public class HibernateL2CacheExampleNodeStartup { + /** + * Start up an empty node with specified cache configuration. + * + * @param args Command line arguments, none required. + * @throws IgniteException If example execution failed. + */ + public static void main(String[] args) throws IgniteException { + Ignition.start(configuration()); + } + + /** + * Create Ignite configuration with IGFS and enabled IPC. + * + * @return Ignite configuration. + * @throws IgniteException If configuration creation failed. + */ + public static IgniteConfiguration configuration() throws IgniteException { + IgniteConfiguration cfg = new IgniteConfiguration(); + + cfg.setGridName("hibernate-grid"); + cfg.setLocalHost("127.0.0.1"); + cfg.setConnectorConfiguration(null); + + TcpDiscoverySpi discoSpi = new TcpDiscoverySpi(); + + TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryVmIpFinder(); + + ipFinder.setAddresses(Collections.singletonList("127.0.0.1:47500..47509")); + + discoSpi.setIpFinder(ipFinder); + + cfg.setDiscoverySpi(discoSpi); + + cfg.setCacheConfiguration( + cacheConfiguration("org.hibernate.cache.spi.UpdateTimestampsCache", ATOMIC), + cacheConfiguration("org.hibernate.cache.internal.StandardQueryCache", ATOMIC), + cacheConfiguration("org.apache.ignite.examples.datagrid.hibernate.User", TRANSACTIONAL), + cacheConfiguration("org.apache.ignite.examples.datagrid.hibernate.User.posts", TRANSACTIONAL), + cacheConfiguration("org.apache.ignite.examples.datagrid.hibernate.Post", TRANSACTIONAL) + ); + + return cfg; + } + + /** + * Create cache configuration. + * + * @param name Cache name. + * @param atomicityMode Atomicity mode. + * @return Cache configuration. + */ + private static CacheConfiguration cacheConfiguration(String name, CacheAtomicityMode atomicityMode) { + CacheConfiguration ccfg = new CacheConfiguration(); + + ccfg.setName(name); + ccfg.setCacheMode(PARTITIONED); + ccfg.setAtomicityMode(atomicityMode); + ccfg.setWriteSynchronizationMode(FULL_SYNC); + + return ccfg; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/be0e755b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/Post.java ---------------------------------------------------------------------- diff --git a/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/Post.java b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/Post.java new file mode 100644 index 0000000..63a7a48 --- /dev/null +++ b/examples/src/main/java/org/apache/ignite/examples/java7/datagrid/hibernate/Post.java @@ -0,0 +1,126 @@ +/* + * 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.examples.java7.datagrid.hibernate; + +import javax.persistence.*; +import java.util.*; + +/** + * An entity class representing a post, that a + * {@link User} has made on some public service. + */ +@Entity +class Post { + /** ID. */ + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + private long id; + + /** Author. */ + @ManyToOne + private User author; + + /** Text. */ + private String text; + + /** Created timestamp. */ + private Date created; + + /** + * Default constructor (required by Hibernate). + */ + Post() { + // No-op. + } + + /** + * Constructor. + * + * @param author Author. + * @param text Text. + */ + Post(User author, String text) { + this.author = author; + this.text = text; + created = new Date(); + } + + /** + * @return ID. + */ + public long getId() { + return id; + } + + /** + * @param id New ID. + */ + public void setId(long id) { + this.id = id; + } + + /** + * @return Author. + */ + public User getAuthor() { + return author; + } + + /** + * @param author New author. + */ + public void setAuthor(User author) { + this.author = author; + } + + /** + * @return Text. + */ + public String getText() { + return text; + } + + /** + * @param text New text. + */ + public void setText(String text) { + this.text = text; + } + + /** + * @return Created timestamp. + */ + public Date getCreated() { + return (Date)created.clone(); + } + + /** + * @param created New created timestamp. + */ + public void setCreated(Date created) { + this.created = (Date)created.clone(); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "Post [id=" + id + + ", text=" + text + + ", created=" + created + + ']'; + } +}