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("jbos‌​s1ind1", 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 @@ + + + + + + + + + + + +