diff --git a/.gitignore b/.gitignore
index 2676acb..f8d5629 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.idea
target
# Compiled class file
*.class
diff --git a/README b/README
new file mode 100644
index 0000000..2330129
--- /dev/null
+++ b/README
@@ -0,0 +1,19 @@
+docker run --name mongodb -p 27017:27017 -d mongo --storageEngine wiredTiger
+docker run --name castrato -p 9042:9042 -p 9162:9162 -p 7000:7000 -e CASSANDRA_START_RPC=true -d stratio/cassandra-lucene-index:3.10.0
+
+drop table books; drop table products; drop table inventory; drop table magazines; desc tables;
+
+mvn clean compile -Dmaven.compiler.showDeprecation=true -Dmaven.compiler.showWarnings=true
+
+https://www.one-tab.com/page/K4XlsjF2TyugqYE_8ORqxg
+
+
+
+
+https://apple.stackexchange.com/questions/215919/enable-multicast-on-mac
+ifconfig -a
+sudo route -nv add -net 239.9.9.9 -interface en0
+netstat -nr | grep 239.9.9.9
+ping -t 1 -c 2 239.9.9.9
+sudo tcpdump -vvv -ni en0 host 239.9.9.9
+sudo route -v delete -inet 239.9.9.9
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 2b89414..0000000
--- a/build.xml
+++ /dev/null
@@ -1,154 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/crud.iml b/crud.iml
index 54c2435..d935a7b 100644
--- a/crud.iml
+++ b/crud.iml
@@ -15,6 +15,15 @@
+
+
+
+
+
@@ -34,6 +43,7 @@
+
@@ -57,20 +67,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 79c53e9..77f17f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,8 +11,13 @@
UTF-8
- 1.8
- 1.8
+ 1.8
+ ${java.version}
+ ${java.version}
+ [5.1.0-m3, 5.9)
+ 1.8.10
+ 5.0.0.M5
+ Kay-M4
@@ -26,22 +31,46 @@
+
+
+
+
+ org.springframework
+ spring-framework-bom
+ ${spring.version}
+ import
+ pom
+
+
+
+ org.springframework.data
+ spring-data-releasetrain
+ ${spring-data.version}
+ import
+ pom
+
+
+
+
+
+
org.datanucleus
datanucleus-core
- [5.0.0-m1, 5.9)
+ ${org.datanucleus.version}
org.datanucleus
datanucleus-api-jpa
- [5.0.0-m1, 5.9)
+ ${org.datanucleus.version}
org.datanucleus
javax.persistence
2.1.0
+
ch.qos.logback
logback-classic
@@ -52,82 +81,22 @@
logback-access
1.2.3
+
+ org.slf4j
+ jcl-over-slf4j
+ 1.7.25
+
org.slf4j
log4j-over-slf4j
1.7.7
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
org.datanucleus
datanucleus-mongodb
- [5.0.0-m1, 5.9)
+ ${org.datanucleus.version}
org.mongodb
@@ -135,63 +104,37 @@
3.4.2
-
-
-
-
-
-
-
+
org.datanucleus
datanucleus-cassandra
- [5.0.0-m1, 5.9)
+ ${org.datanucleus.version}
com.datastax.cassandra
cassandra-driver-core
- 3.0.2
+ 3.2.0
-
+
+ org.infinispan
+ infinispan-embedded
+ 9.0.2.Final
+
+
+ org.jboss.slf4j
+ slf4j-jboss-logging
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+ log4j
+ log4j
+
+
+
+
+
+ org.infinispan
+ infinispan-jcache
+ 9.0.2.Final
+
+
+
+ javax.cache
+ cache-api
+ 1.0.0
+
+
+
+ org.ehcache
+ ehcache
+ 3.3.1
+
+
+
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
org.springframework.data
spring-data-jpa
- 2.0.0.M4
+
+
+
+
+ org.springframework.data
+ spring-data-keyvalue
+
+
+
+
+ org.springframework
+ spring-orm
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-tx
+ ${spring.version}
+
+
+
+ org.springframework.security
+ spring-security-web
+ 5.0.0.M2
+
+
+
+ org.springframework
+ spring-aspects
+ ${spring.version}
- com.datastax.cassandra
- cassandra-driver-core
- 3.2.0
+ joda-time
+ joda-time
+ 2.9.9
+
org.projectlombok
lombok
@@ -224,6 +263,19 @@
4.12
test
+
+
+ io.astefanutti.metrics.aspectj
+ metrics-aspectj
+ 1.2.0
+
+
+
+ org.glassfish.web
+ javax.el
+ 2.2.6
+
+
@@ -237,10 +289,6 @@
${basedir}
*.log
- tutorial.xls
- tutorial.ods
- tutorial.ooxml
- tutorial.xml
@@ -253,6 +301,65 @@
maven-compiler-plugin
3.5.1
+
+ org.codehaus.mojo
+ aspectj-maven-plugin
+ 1.10
+
+
+
+ io.astefanutti.metrics.aspectj
+ metrics-aspectj
+
+
+
+
+
+ process-classes
+
+ compile
+ test-compile
+
+
+
+ true
+
+
+ ${project.build.directory}/classes
+
+ ${java.version}
+ ${java.version}
+ ${java.version}
+ ignore
+
+
+
+
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+ org.aspectj
+ aspectjtools
+ ${aspectj.version}
+
+
+
+
+
org.codehaus.mojo
exec-maven-plugin
diff --git a/src/main/java/com/example/crud/ApplicationConfig.java b/src/main/java/com/example/crud/ApplicationConfig.java
index aa8c26e..a616a5d 100644
--- a/src/main/java/com/example/crud/ApplicationConfig.java
+++ b/src/main/java/com/example/crud/ApplicationConfig.java
@@ -1,32 +1,102 @@
package com.example.crud;
+import com.example.crud.entities.AbstractAuditableEntity;
+import com.example.crud.entities.AbstractEntity;
+import com.example.crud.entities.Product;
+import org.datanucleus.enhancer.DataNucleusEnhancer;
+import org.infinispan.configuration.cache.CacheMode;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.configuration.global.GlobalConfigurationBuilder;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.manager.EmbeddedCacheManager;
+import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
+import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
+import java.net.UnknownHostException;
@Configuration
@EnableJpaRepositories
@EnableJpaAuditing
+@EnableScheduling
+@EnableAspectJAutoProxy
+@EnableCaching
@EnableTransactionManagement
class ApplicationConfig {
- @Bean
- public EntityManagerFactory entityManagerFactory() {
- EntityManagerFactory emf = Persistence.createEntityManagerFactory("crud");
- return emf;
- }
+ @PostConstruct
+ private void enhanceModelObjectBytecode() {
+ DataNucleusEnhancer enhancer = new DataNucleusEnhancer("JPA", null);
+ enhancer.setVerbose(true);
+ enhancer.addClasses(AbstractEntity.class.getName());
+ enhancer.addClasses(AbstractAuditableEntity.class.getName());
+ enhancer.addClasses(Product.class.getName());
+ enhancer.addPersistenceUnit("crud");
+ enhancer.addPersistenceUnit("mongo");
+ enhancer.enhance();
+ }
+
+ @Bean
+ public EntityManagerFactory entityManagerFactory() {
+ EntityManagerFactory emf = Persistence.createEntityManagerFactory("crud");
+ return emf;
+ }
+
+ @Bean
+ public PlatformTransactionManager transactionManager() {
+ JpaTransactionManager txManager = new JpaTransactionManager();
+ txManager.setEntityManagerFactory(entityManagerFactory());
+ return txManager;
+ }
+
+ @Bean
+ public EmbeddedCacheManager cacheManager() {
+ return infinispanEmbeddedDistributedCacheManager();
+ }
+
+ @Bean
+ public AuditorAware auditorAware() {
+ return new UsernameAuditorAware();
+ }
+
+ private EmbeddedCacheManager infinispanEmbeddedDistributedCacheManager() {
+ String nodeName = null;
+ try {
+ nodeName = java.net.InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ nodeName = "localhost";
+ }
+// ConfigurationBuilder cb = new ConfigurationBuilder(); cb.addCluster("HighQCacheCluster").addClusterNode("jboss1ind1", 11222).addClusterNode("udit.local.com", 11222); RemoteCacheManager rmc = new RemoteCacheManager(cb.build());
+
+ DefaultCacheManager cacheManager = new DefaultCacheManager(
+ GlobalConfigurationBuilder.defaultClusteredBuilder()
+ .transport().nodeName(nodeName).addProperty("configurationFile",
+ "jgroups-l2-cache-udp-largecluster.xml")
+ .build(),
+ new ConfigurationBuilder()
+ .clustering()
+ .cacheMode(CacheMode.INVALIDATION_SYNC)
+ .build()
+ );
+ // The only way to get the "repl" cache to be exactly the same as the default cache is to not define it at all
+ cacheManager.defineConfiguration("dist", new ConfigurationBuilder()
+ .clustering()
+ .cacheMode(CacheMode.DIST_SYNC)
+ .hash().numOwners(2)
+ .build()
+ );
+ return cacheManager;
+ }
- @Bean
- public PlatformTransactionManager transactionManager() {
- JpaTransactionManager txManager = new JpaTransactionManager();
- txManager.setEntityManagerFactory(entityManagerFactory());
- return txManager;
- }
}
diff --git a/src/main/java/com/example/crud/CacheClusterListener.java b/src/main/java/com/example/crud/CacheClusterListener.java
new file mode 100644
index 0000000..5feeb5b
--- /dev/null
+++ b/src/main/java/com/example/crud/CacheClusterListener.java
@@ -0,0 +1,52 @@
+package com.example.crud;
+
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
+import org.infinispan.notifications.cachelistener.annotation.TopologyChanged;
+import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
+import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
+import org.infinispan.notifications.cachelistener.event.TopologyChangedEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+@Listener(clustered = true)
+public class CacheClusterListener {
+ private Logger log = LoggerFactory.getLogger(getClass().getName());
+
+ @CacheEntryCreated
+ public void observeAdd(CacheEntryCreatedEvent event) {
+ if (event.isPre())
+ return;
+
+ log.info("Cache entry %s added in cache %s", event.getKey(), event.getCache());
+ }
+
+ @CacheEntryModified
+ public void observeUpdate(CacheEntryModifiedEvent event) {
+ if (event.isPre())
+ return;
+
+ log.info("Cache entry %s = %s modified in cache %s", event.getKey(), event.getValue(), event.getCache());
+ }
+
+ @CacheEntryRemoved
+ public void observeRemove(CacheEntryRemovedEvent event) {
+ if (event.isPre())
+ return;
+
+ log.info("Cache entry %s removed in cache %s", event.getKey(), event.getCache());
+ }
+
+ @TopologyChanged
+ public void observeTopologyChange(TopologyChangedEvent event) {
+ if (event.isPre())
+ return;
+
+ log.info("Cache %s topology changed, new membership is %s", event.getCache().getName(), event.getConsistentHashAtEnd().getMembers());
+ }
+
+}
diff --git a/src/main/java/com/example/crud/JGroups.java b/src/main/java/com/example/crud/JGroups.java
new file mode 100644
index 0000000..a040a92
--- /dev/null
+++ b/src/main/java/com/example/crud/JGroups.java
@@ -0,0 +1,236 @@
+package com.example.crud;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.regex.Pattern;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.jgroups.*;
+import org.jgroups.blocks.locking.LockService;
+
+public class JGroups {
+ private Logger log = LoggerFactory.getLogger(getClass().getName());
+ /* this variable indicates whether I have become the master or I'm just a client*/
+ public volatile AtomicBoolean becomeMaster = new AtomicBoolean(false);
+ /* The address of the server if we are a client or of ourself if we are
+ * server */
+ public String serverAddress;
+ /* A channel on which to acquire a lock, so that only one can become server */
+ private JChannel lockChannel;
+ /* A shared channel ffor communication between client and master*/
+ private JChannel communicationChannel;
+ private LockService lockService;
+ /* A thread which tries to acquire a lock */
+ private Thread acquiringThread;
+ /* A thread which listens for the server ip which may change */
+ private Thread listeningThread;
+ /* A thread which lists the status and initializes the acquiring thread*/
+ private Thread statusThread;
+ private String name;
+ /* If we pass from being a client to being a server we must stop the listening
+ * thread however we cannot call listeningThread.stop() but instead we change
+ * the stopListening boolean to true */
+ private boolean stopListening = false;
+ /* This lock communicates I have finally become either master or client so
+ * the serverAddress and becomeMaster variables are correctly set */
+ public final Object finishedLock = new Object();
+
+ public static void main(String[] args) throws Exception {
+ //System.setProperty("jgroups.udp.mcast_addr", "127.0.0.1");
+ Thread.currentThread().setName("MyMainThread");
+ Random rand = new Random();
+
+ JGroups master = new JGroups("Node" + rand.nextInt(10));
+
+ master.lockChannel = new JChannel(JGroups.class.getClassLoader().getResource(
+ "jgroups-l2-cache-udp-largecluster.xml"));
+ master.lockChannel.connect("lock-channel");
+
+ master.communicationChannel = new JChannel(
+ JGroups.class.getClassLoader().getResource("jgroups-l2-cache-udp-largecluster.xml"));
+ master.communicationChannel.connect("communication-channel");
+
+ master.lockService = new LockService(master.lockChannel);
+ master.startStatusPrinterThread();
+ }
+
+ public JGroups(String name) {
+ this.name = name;
+ }
+
+ public JGroups() {
+ try {
+ Thread.currentThread().setName("MyMainThread");
+ Random rand = new Random();
+
+ this.name = ("Node" + rand.nextInt(10));
+
+ lockChannel = new JChannel(JGroups.class.getClassLoader().getResource("/resource/udp.xml"));
+ lockChannel.connect("lock-channel");
+
+ communicationChannel = new JChannel(JGroups.class.getClassLoader().getResource("/resource/udp.xml"));
+ communicationChannel.connect("communication-channel");
+
+ lockService = new LockService(lockChannel);
+ startStatusPrinterThread();
+ }
+ catch (Exception ex) {
+ log.error(ex.getStackTrace().toString());
+ }
+ }
+
+ public void startAcquiringThread() {
+ acquiringThread = new Thread() {
+ @Override
+ public void run() {
+ while (true) {
+ //if you have become Master send your ip every now and then
+ if (becomeMaster.get()) {
+ try {
+ StringBuffer buffer = new StringBuffer("serverip " + serverAddress);
+ communicationChannel.send(new Message(null, buffer));
+ }
+ catch (Exception ex) {
+ log.error(ex.getStackTrace().toString());
+ }
+ } else {
+ try {
+ Thread.currentThread().setName(name + "AcquiringThread");
+ Lock lock = lockService.getLock("serverLock");
+ if (lock.tryLock(4, TimeUnit.SECONDS)) {
+ becomeMaster.set(true);
+ stopListening = true;
+ /* Now that I'm server I must find out my own ip address on which to listen */
+ Enumeration networkInterfaces;
+ try {
+ networkInterfaces = NetworkInterface.getNetworkInterfaces();
+ for (NetworkInterface netint : Collections.list(networkInterfaces)) {
+ Enumeration inetAddresses = netint.getInetAddresses();
+ for (InetAddress inetAddress : Collections.list(inetAddresses)) {
+ if (isIPAddress(inetAddress.getHostAddress())
+ && !inetAddress.getHostAddress().equals("127.0.0.1")) {
+ serverAddress = inetAddress.getHostAddress();
+ }
+ }
+ }
+ /* I notify to the rest of the program I have correctly initialized
+ * becomeMaster and serverAddress */
+ synchronized (finishedLock) {
+ finishedLock.notify();
+ }
+ }
+ catch (Exception ex) {
+ log.error(ex.getStackTrace().toString());
+ System.exit(0);
+ }
+ log.info(Thread.currentThread().getName()
+ + ": I acquired lock! will become master! my ip is " + serverAddress);
+ } else {
+ becomeMaster.set(false);
+ stopListening = false;
+ if (listeningThread == null || !listeningThread.isAlive()) {
+ if (!stopListening) {
+ //??? this codnition might be useless
+ startListeningThread();
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ sleep(5000L);
+ }
+ catch (InterruptedException ex) {
+ log.error(ex.getStackTrace().toString());
+ }
+ }
+ }
+ };
+ acquiringThread.setDaemon(true);
+ acquiringThread.start();
+ }
+
+ public void startListeningThread() {
+ listeningThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ Thread.currentThread().setName(name + "ListeningThread");
+ communicationChannel.setReceiver(new ReceiverAdapter() {
+ @Override
+ public void receive(Message msg) {
+ if (msg.getObject() != null) {
+ String leaderServerAddress = (msg.getObject().toString().substring(9));
+ if (isIPAddress(leaderServerAddress)) {
+ serverAddress = leaderServerAddress;
+ log.info(name + " Master server has ip" + serverAddress);
+ /* I notify to the rest of the program I have correctly initialized
+ * becomeMaster and serverAddress */
+ synchronized (finishedLock) {
+ finishedLock.notify();
+ }
+ } else {
+ log.info(name + ": discarded message " + msg.getObject().toString());
+ }
+ }
+ }
+ });
+ sleep(10000L);
+ if (stopListening) {
+ return;
+ }
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ };
+ listeningThread.setDaemon(true);
+ listeningThread.start();
+ }
+
+ private void startStatusPrinterThread() {
+ statusThread = new Thread() {
+ @Override
+ public void run() {
+ Thread.currentThread().setName(name + "StatusPrinterThread");
+ startAcquiringThread();
+ while (true) {
+ try {
+ if (becomeMaster.get()) {
+ log.info(name + " startStatusPrinterThread(): I am happily a Master!");
+ } else {
+ if (!acquiringThread.isAlive()) {
+ startAcquiringThread();
+ }
+ }
+ sleep(5000L);
+ }
+ catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+ statusThread.setDaemon(true);
+ statusThread.start();
+ }
+
+ private static boolean isIPAddress(String str) {
+ Pattern ipPattern = Pattern.compile("^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
+ return ipPattern.matcher(str).matches();
+ }
+}
diff --git a/src/main/java/com/example/crud/Main.java b/src/main/java/com/example/crud/Main.java
index d93d1a7..4641132 100644
--- a/src/main/java/com/example/crud/Main.java
+++ b/src/main/java/com/example/crud/Main.java
@@ -1,40 +1,62 @@
package com.example.crud;
+import com.example.crud.entities.*;
+import com.example.crud.repositories.InventoryRepository;
+import org.datanucleus.util.NucleusLogger;
+import org.infinispan.Cache;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.manager.DefaultCacheManager;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
+
+import javax.persistence.*;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.HashMap;
-
-import javax.persistence.EntityManager;
-import javax.persistence.EntityManagerFactory;
-import javax.persistence.EntityTransaction;
-import javax.persistence.EntityGraph;
-import javax.persistence.Persistence;
-import javax.persistence.Query;
-
-import com.example.crud.entities.*;
-
-import com.example.crud.repositories.InventoryRepository;
-import org.datanucleus.enhancer.DataNucleusEnhancer;
-import org.datanucleus.util.NucleusLogger;
-import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
/**
* Controlling application for the DataNucleus Tutorial using JPA.
* Uses the "persistence-unit" called "Tutorial".
*/
public class Main {
+
+ public static void cacheTest() {
+ // Construct a simple local cache manager with default configuration
+ DefaultCacheManager cacheManager = new DefaultCacheManager();
+ // Define local cache configuration
+ cacheManager.defineConfiguration("local", new ConfigurationBuilder().build());
+ // Obtain the local cache
+ Cache cache = cacheManager.getCache("local");
+ // Register a listener
+ cache.addListener(new CacheClusterListener());
+ // Store some values
+ cache.put("key1", "value1");
+ cache.put("key2", "value2");
+ cache.put("key1", "newValue");
+ // Stop the cache manager and release all resources
+ cacheManager.stop();
+ }
+
public static void main(String args[]) {
- DataNucleusEnhancer enhancer = new DataNucleusEnhancer("JPA", null);
- enhancer.setVerbose(true);
- enhancer.addPersistenceUnit("crud");
- enhancer.addPersistenceUnit("mongo");
- enhancer.enhance();
+ //cacheTest();
+
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.register(ApplicationConfig.class);
+ ctx.refresh();
// Create an EntityManagerFactory for this "persistence-unit"
// See the file "META-INF/persistence.xml"
EntityManagerFactory emf = Persistence.createEntityManagerFactory("crud");
EntityManagerFactory emf_mongo = Persistence.createEntityManagerFactory("mongo");
+ //MergingPersistenceUnitmanager
+
+ // TODO:
+ // * types: int, bool, etc.
+ // * Set<>
+ // * L2/Caching via Infinispan (embedded, clustered)
+ // * MergingPersistenceUnitmanager
+ // * Draft/(Fluent)Builder Immutable Entites
// Persistence of a Product and a Book.
EntityManager em = emf.createEntityManager();
@@ -44,16 +66,18 @@ public class Main {
Inventory inv = em.merge(new Inventory("My Inventory"));
Product product = new Product("Sony Discman", "A standard discman from Sony", 200.00);
- inv.getProducts().add(product);
+ inv.addProduct(product);
Book book = new Book("Lord of the Rings by Tolkien", "The classic story", 49.99, "JRR Tolkien",
"12345678", "MyBooks Factory");
Magazine magazine = new Magazine("Field and Stream", "A hunter's guide to the outdoors.", 3.29, "F&S, Inc.", "23984729347", "F&S, Inc.");
- inv.getProducts().add(book);
- inv.getProducts().add(magazine);
+ inv.addProduct(book);
+ inv.addProduct(magazine);
em.persist(inv);
tx.commit();
- System.out.println("Product and Book have been persisted");
+// SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(inv);
+// SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(product);
+// System.out.println("Product and Book have been persisted, inventory: " + inv.getPrimaryKey().toString() + ", product: " + product.getPrimaryKey().toString());
}
catch (Exception e) {
NucleusLogger.GENERAL.error(">> Exception persisting data", e);
@@ -168,6 +192,7 @@ public class Main {
em.close();
// Clean out the database
+ emf.getCache().evictAll();
em = emf.createEntityManager();
tx = em.getTransaction();
try {
@@ -177,7 +202,7 @@ public class Main {
inv = (Inventory) em.find(Inventory.class, "My Inventory");
System.out.println("Clearing out Inventory");
- inv.getProducts().clear();
+ inv.clearProducts();
em.flush();
System.out.println("Deleting Inventory");
diff --git a/src/main/java/com/example/crud/UsernameAuditorAware.java b/src/main/java/com/example/crud/UsernameAuditorAware.java
new file mode 100644
index 0000000..0066b32
--- /dev/null
+++ b/src/main/java/com/example/crud/UsernameAuditorAware.java
@@ -0,0 +1,26 @@
+package com.example.crud;
+
+import org.springframework.data.domain.AuditorAware;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import java.util.Optional;
+
+public class UsernameAuditorAware implements AuditorAware {
+
+ @Override
+ public Optional getCurrentAuditor() {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+ if (authentication == null || !authentication.isAuthenticated()) {
+ return Optional.empty();
+ }
+ User user = (User)authentication.getPrincipal();
+ String username = null;
+ if (user != null) {
+ username = user.getUsername();
+ }
+ return (username == null) ? Optional.empty() : Optional.of(username);
+ }
+}
diff --git a/src/main/java/com/example/crud/entities/AbstractAuditableEntity.java b/src/main/java/com/example/crud/entities/AbstractAuditableEntity.java
new file mode 100644
index 0000000..21c2040
--- /dev/null
+++ b/src/main/java/com/example/crud/entities/AbstractAuditableEntity.java
@@ -0,0 +1,34 @@
+package com.example.crud.entities;
+
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.EntityListeners;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import java.io.Serializable;
+import java.util.Date;
+
+@MappedSuperclass
+@EntityListeners({AuditingEntityListener.class})
+public abstract class AbstractAuditableEntity extends AbstractEntity {
+
+ @CreatedDate
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date createdDateTime;
+
+ @LastModifiedDate
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date lastModifiedDateTime;
+
+ @CreatedBy
+ private String createdByUser;
+
+ @LastModifiedBy
+ private String modifiedByUser;
+
+}
diff --git a/src/main/java/com/example/crud/entities/AbstractEntity.java b/src/main/java/com/example/crud/entities/AbstractEntity.java
new file mode 100644
index 0000000..8fd9a1a
--- /dev/null
+++ b/src/main/java/com/example/crud/entities/AbstractEntity.java
@@ -0,0 +1,26 @@
+package com.example.crud.entities;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+@Data
+@MappedSuperclass
+public abstract class AbstractEntity {
+
+ @Transient
+ @Autowired
+ EntityManagerFactory emf;
+
+ @Version
+ protected long version;
+
+ public ID getPrimaryKey() {
+ final PersistenceUnitUtil util = emf.getPersistenceUnitUtil();
+ Object id = util.getIdentifier(this);
+ return (ID)id;
+ }
+
+}
diff --git a/src/main/java/com/example/crud/entities/Inventory.java b/src/main/java/com/example/crud/entities/Inventory.java
index 3177ac6..8324f98 100644
--- a/src/main/java/com/example/crud/entities/Inventory.java
+++ b/src/main/java/com/example/crud/entities/Inventory.java
@@ -1,34 +1,45 @@
package com.example.crud.entities;
+import com.google.common.collect.ImmutableSet;
import lombok.Data;
import lombok.ToString;
+import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
-import javax.persistence.*;
-
/**
* Definition of an Inventory of products.
*/
@Data @Entity
@ToString
-@NamedEntityGraph(name="allProps",
- attributeNodes={@NamedAttributeNode("name"), @NamedAttributeNode("products")})
-public class Inventory
-{
+@NamedEntityGraph(name = "allProps",
+ attributeNodes = { @NamedAttributeNode("name"), @NamedAttributeNode("products") })
+public class Inventory extends AbstractAuditableEntity {
+
@Id
private String name=null;
- @OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH}, fetch=FetchType.EAGER)
- public Set products = new HashSet();
+ @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH }, fetch = FetchType.EAGER)
+ private Set products = new HashSet();
- @Version
- long version;
+ public Inventory() {
+ }
- public Inventory() { }
- public Inventory(String name)
- {
+ public Inventory(String name) {
this.name = name;
}
+
+ public void addProduct(Product product) {
+ products.add(product);
+ }
+
+ public Iterable getProducts() {
+ return ImmutableSet.copyOf(products);
+ }
+
+ public void clearProducts() {
+ products.clear();
+ }
+
}
diff --git a/src/main/java/com/example/crud/entities/Magazine.java b/src/main/java/com/example/crud/entities/Magazine.java
index 34b7b04..d713a6e 100644
--- a/src/main/java/com/example/crud/entities/Magazine.java
+++ b/src/main/java/com/example/crud/entities/Magazine.java
@@ -7,27 +7,30 @@ import lombok.ToString;
import javax.persistence.Basic;
import javax.persistence.Entity;
-import javax.persistence.Index;
-import javax.persistence.Table;
/**
* Definition of a Book. Extends basic Product class.
**/
@Data @Entity
-@EqualsAndHashCode(callSuper=false)
+@EqualsAndHashCode(callSuper = false)
@ToString
@AllArgsConstructor
-public class Magazine extends Product
-{
- /** Author of the Book. */
+public class Magazine extends Product {
+ /**
+ * Author of the Book.
+ */
@Basic
private String author = null;
- /** ISBN number of the book. */
+ /**
+ * ISBN number of the book.
+ */
@Basic
private String isbn = null;
- /** Publisher of the Book. */
+ /**
+ * Publisher of the Book.
+ */
@Basic
private String publisher = null;
@@ -40,20 +43,21 @@ public class Magazine extends Product
/**
* Constructor.
- * @param name name of product
+ *
+ * @param name name of product
* @param description description of product
- * @param price Price
- * @param author Author of the book
- * @param isbn ISBN number of the book
- * @param publisher Name of publisher of the book
+ * @param price Price
+ * @param author Author of the book
+ * @param isbn ISBN number of the book
+ * @param publisher Name of publisher of the book
**/
public Magazine(String name,
- String description,
- double price,
- String author,
- String isbn,
- String publisher) {
- super(name,description,price);
+ String description,
+ double price,
+ String author,
+ String isbn,
+ String publisher) {
+ super(name, description, price);
this.author = author;
this.isbn = isbn;
this.publisher = publisher;
diff --git a/src/main/java/com/example/crud/entities/Person.java b/src/main/java/com/example/crud/entities/Person.java
index 3d4e45a..37342f6 100644
--- a/src/main/java/com/example/crud/entities/Person.java
+++ b/src/main/java/com/example/crud/entities/Person.java
@@ -8,66 +8,64 @@ import java.util.List;
*/
@Entity
@Cacheable
-@Table( name = "PERSON",
+@Table(name = "PERSON",
indexes = {
- @Index(name = "last_name_idx", columnList="PERSON_LAST_NAME", unique = false)
- /*, @Index(name = "email_idx", columnList="EMAIL", unique = false)*/})
-public class Person
-{
- /** The person id. */
- @Id @GeneratedValue(strategy=GenerationType.AUTO)
- @Column(name = "PERSON_ID")
+ @Index(name = "last_name_idx", columnList = "PERSON_LAST_NAME", unique = false)
+ /*, @Index(name = "email_idx", columnList="EMAIL", unique = false)*/ })
+public class Person extends AbstractAuditableEntity {
+ /**
+ * The person id.
+ */
+ @Id @GeneratedValue(strategy = GenerationType.AUTO)
+ @Column(name = "ID")
private String personId;
- /** The person first name. */
+ /**
+ * The person first name.
+ */
@Column(name = "PERSON_FIRST_NAME")
private String personFirstName;
- /** The person last name. */
+ /**
+ * The person last name.
+ */
@Column(name = "PERSON_LAST_NAME", nullable = false)
private String personLastName;
- /** The age. */
+ /**
+ * The age.
+ */
@Column(name = "AGE")
private int age;
- /** Email addresses.
- @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
- @JoinColumn(name = "EMAIL")
- @Transient
- private Set email = new HashSet<>();
-
- @Column(name = "UPDATED")
- @Temporal(TemporalType.DATE)
- private Date updated;
+ /**
+ * Email addresses.
+ *
+ * @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
+ * @JoinColumn(name = "EMAIL")
+ * @Transient private Set email = new HashSet<>();
+ * @Column(name = "UPDATED")
+ * @Temporal(TemporalType.DATE) private Date updated;
*/
@OneToMany(mappedBy = "seller")
private List products;
- @Column(name = "VERSION")
- @Version
- private long version;
-
-
/**
* Gets the person id.
*
* @return the person id
*/
- public String getPersonId()
- {
+ public String getPersonId() {
return personId;
}
/**
* Sets the person id.
*
- * @param personId
- * the new person id
+ * @param personId the new person id
*/
- public void setPersonId(String personId)
- {
+ public void setPersonId(String personId) {
this.personId = personId;
}
@@ -76,8 +74,7 @@ public class Person
*
* @return the person name
*/
- public String getPersonName()
- {
+ public String getPersonName() {
return personFirstName + " " + personLastName;
}
@@ -86,19 +83,16 @@ public class Person
*
* @return the person first name
*/
- public String getPersonFirstName()
- {
+ public String getPersonFirstName() {
return personFirstName;
}
/**
* Sets the person first name.
*
- * @param personFirstName
- * the new person first name
+ * @param personFirstName the new person first name
*/
- public void setPersonFirstName(String personFirstName)
- {
+ public void setPersonFirstName(String personFirstName) {
this.personFirstName = personFirstName;
}
@@ -107,19 +101,16 @@ public class Person
*
* @return the person last name
*/
- public String getPersonLastName()
- {
+ public String getPersonLastName() {
return personLastName;
}
/**
* Sets the person last name.
*
- * @param personLastName
- * the new person last name
+ * @param personLastName the new person last name
*/
- public void setPersonLastName(String personLastName)
- {
+ public void setPersonLastName(String personLastName) {
this.personLastName = personLastName;
}
@@ -128,22 +119,19 @@ public class Person
*
* @return the age
*/
- public int getAge()
- {
+ public int getAge() {
return age;
}
/**
* Sets the age.
*
- * @param age
- * the new age
+ * @param age the new age
*/
- public void setAge(int age)
- {
+ public void setAge(int age) {
this.age = age;
}
-// public void addEmail(String email) { this.email.add(email); }
+ // public void addEmail(String email) { this.email.add(email); }
}
diff --git a/src/main/java/com/example/crud/entities/Product.java b/src/main/java/com/example/crud/entities/Product.java
index 0b7af1d..74ebff2 100644
--- a/src/main/java/com/example/crud/entities/Product.java
+++ b/src/main/java/com/example/crud/entities/Product.java
@@ -3,7 +3,6 @@ package com.example.crud.entities;
import lombok.Data;
import lombok.ToString;
-import javax.annotation.Generated;
import javax.persistence.*;
/**
@@ -12,40 +11,51 @@ import javax.persistence.*;
**/
@Data @Entity
@ToString
-@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
-public class Product
-{
- /** Id for the product. */
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+public class Product extends AbstractAuditableEntity {
+
+ /**
+ * Id for the product.
+ */
@Id
private String id;
- /** Name of the Product. */
+ /**
+ * Name of the Product.
+ */
@Basic
- private String name=null;
+ private String name = null;
- /** Description of the Product. */
+ /**
+ * Description of the Product.
+ */
@Basic
- private String description=null;
+ private String description = null;
- /** Price of the Product. */
+ /**
+ * Price of the Product.
+ */
@Basic
- private double price=0.0;
+ private double price = 0.0;
- /** Seller of this product. */
+ /**
+ * Seller of this product.
+ */
@ManyToOne(optional = false)
private Person seller;
/**
- * Default constructor.
+ * Default constructor.
*/
protected Product() {
}
/**
* Constructor.
- * @param name name of product
+ *
+ * @param name name of product
* @param description description of product
- * @param price Price
+ * @param price Price
**/
public Product(String name, String description, double price) {
this.name = name;
@@ -53,4 +63,12 @@ public class Product
this.price = price;
}
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
}
diff --git a/src/main/java/com/example/crud/repositories/InventoryRepository.java b/src/main/java/com/example/crud/repositories/InventoryRepository.java
index 678a1bd..ecaa720 100644
--- a/src/main/java/com/example/crud/repositories/InventoryRepository.java
+++ b/src/main/java/com/example/crud/repositories/InventoryRepository.java
@@ -1,19 +1,34 @@
package com.example.crud.repositories;
+import com.codahale.metrics.annotation.Metered;
import com.example.crud.entities.Inventory;
-import com.example.crud.entities.Product;
+import io.astefanutti.metrics.aspectj.Metrics;
+import org.datanucleus.api.jpa.annotations.ReadOnly;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
+import org.springframework.transaction.annotation.Transactional;
import java.util.List;
-public interface InventoryRepository extends JpaRepository {
+//@Metrics(registry = "${this.registry}")
+public interface InventoryRepository extends JpaRepository, JpaSpecificationExecutor {
- Inventory findByName(String name);
+ //@Metered(name = "${this.id}")
+ @Transactional
+ @Cacheable(value = "inventory", key = "#name")
+ Inventory findByName(String name);
- @Query(value = "select * from inventory where product_id_eid contains :productId allow filtering",
- nativeQuery = true)
- List findByProduct(@Param("productId") String productId);
+ @ReadOnly
+ @Query(value = "select * from inventory where product_id_eid contains :productId allow filtering",
+ nativeQuery = true)
+ List findByProduct(@Param("productId") String productId);
+
+ @Transactional
+ @CacheEvict(value = "inventory", key = "#name")
+ void deleteInventoryBy(String name);
}
diff --git a/src/main/resources/META-INF/orm.xml b/src/main/resources/META-INF/orm.xml
index 7596b86..24589d7 100644
--- a/src/main/resources/META-INF/orm.xml
+++ b/src/main/resources/META-INF/orm.xml
@@ -6,6 +6,30 @@
JPA Mapping for CRUD/JPA
com.example.crud.entities
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -48,9 +72,6 @@
-
-
-
diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml
index 582585a..6036c4f 100644
--- a/src/main/resources/META-INF/persistence.xml
+++ b/src/main/resources/META-INF/persistence.xml
@@ -25,6 +25,9 @@
+
+
+
diff --git a/src/main/resources/ehcache.xml b/src/main/resources/ehcache.xml
new file mode 100644
index 0000000..1c9f9db
--- /dev/null
+++ b/src/main/resources/ehcache.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/infinispan.xml b/src/main/resources/infinispan.xml
new file mode 100644
index 0000000..083bdfa
--- /dev/null
+++ b/src/main/resources/infinispan.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/jgroups-l2-cache-udp-largecluster.xml b/src/main/resources/jgroups-l2-cache-udp-largecluster.xml
new file mode 100644
index 0000000..b4ab83c
--- /dev/null
+++ b/src/main/resources/jgroups-l2-cache-udp-largecluster.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 865c000..f8a5427 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -17,6 +17,10 @@
+
+
+
+
@@ -38,10 +42,6 @@
-
-
-
-
@@ -82,6 +82,18 @@
+
+
+
+
+
+
+
+
+
+
+
+