From 54caf644a2ad5a502e6265b712a3b23f90c8ab41 Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Thu, 22 Jun 2017 11:40:59 -0400 Subject: [PATCH] L2 cache via EhCache working, disributed via JGroups. --- crud.iml | 60 +++-- pom.xml | 168 +++++++++---- ...fig.java => ApplicationConfiguration.java} | 65 ++--- .../example/crud/CacheClusterListener.java | 52 ---- .../com/example/crud/CacheConfiguration.java | 135 ++++++++++ .../example/crud/EhCacheConfiguration.java | 26 ++ src/main/java/com/example/crud/JGroups.java | 236 ------------------ src/main/java/com/example/crud/Main.java | 191 +++++++++----- .../example/crud/entities/AbstractEntity.java | 12 +- .../java/com/example/crud/entities/Book.java | 3 + .../com/example/crud/entities/Inventory.java | 6 + .../com/example/crud/entities/Person.java | 7 + .../crud/metrics/CoalescingReporter.java | 229 +++++++++++++++++ .../CoalescingReporterElementParser.java | 38 +++ .../CoalescingReporterFactoryBean.java | 54 ++++ .../crud/metrics/MetricsConfiguration.java | 151 +++++++++++ .../crud/metrics/OperatingSystemGaugeSet.java | 125 ++++++++++ .../crud/metrics/SpringConfiguringClass.java | 22 ++ .../repositories/InventoryRepository.java | 6 +- .../crud/repositories/PersonRepository.java | 8 + src/main/resources/META-INF/orm.xml | 4 + src/main/resources/META-INF/persistence.xml | 10 +- src/main/resources/ehcache.xml | 30 ++- src/main/resources/infinispan.xml | 18 -- src/main/resources/logback.xml | 104 +++----- 25 files changed, 1192 insertions(+), 568 deletions(-) rename src/main/java/com/example/crud/{ApplicationConfig.java => ApplicationConfiguration.java} (52%) delete mode 100644 src/main/java/com/example/crud/CacheClusterListener.java create mode 100644 src/main/java/com/example/crud/CacheConfiguration.java create mode 100644 src/main/java/com/example/crud/EhCacheConfiguration.java delete mode 100644 src/main/java/com/example/crud/JGroups.java create mode 100644 src/main/java/com/example/crud/metrics/CoalescingReporter.java create mode 100644 src/main/java/com/example/crud/metrics/CoalescingReporterElementParser.java create mode 100644 src/main/java/com/example/crud/metrics/CoalescingReporterFactoryBean.java create mode 100644 src/main/java/com/example/crud/metrics/MetricsConfiguration.java create mode 100644 src/main/java/com/example/crud/metrics/OperatingSystemGaugeSet.java create mode 100644 src/main/java/com/example/crud/metrics/SpringConfiguringClass.java create mode 100644 src/main/java/com/example/crud/repositories/PersonRepository.java delete mode 100644 src/main/resources/infinispan.xml diff --git a/crud.iml b/crud.iml index d935a7b..add7feb 100644 --- a/crud.iml +++ b/crud.iml @@ -13,16 +13,19 @@ - + + + file://$MODULE_DIR$/src/main/java/com/example/crud/ApplicationConfig.java + file://$MODULE_DIR$/src/main/java/com/example/crud/CacheConfiguration.java + file://$MODULE_DIR$/src/main/java/com/example/crud/EhCacheConfiguration.java + file://$MODULE_DIR$/src/main/java/com/example/crud/metrics/MetricsConfiguration.java + file://$MODULE_DIR$/src/main/java/com/example/crud/metrics/SpringConfiguringClass.java + file://$MODULE_DIR$/applicationContext.xml + + - - - + @@ -55,7 +58,6 @@ - @@ -67,25 +69,21 @@ + - - - - - - - - - - + + - + + + + @@ -102,10 +100,22 @@ - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 77f17f8..7bf786f 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,7 @@ ${java.version} [5.1.0-m3, 5.9) 1.8.10 + 3.2.2 5.0.0.M5 Kay-M4 @@ -29,6 +30,14 @@ false + + clojars.org + http://clojars.org/repo + + + maven.apache.org + http://repo.maven.apache.org/maven2/ + @@ -116,13 +125,26 @@ 3.2.0 + + org.hdrhistogram + HdrHistogram + [2.1.8,) + + org.datanucleus datanucleus-cache 5.0.0-release + - - - 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 + - + + net.sf.ehcache + ehcache-core + 2.6.11 + + + + net.sf.ehcache + ehcache + 2.10.4 + pom + + + + net.sf.ehcache + ehcache-jgroupsreplication + 1.7 + @@ -207,6 +205,12 @@ ${spring.version} + + org.springframework + spring-context-support + ${spring.version} + + org.springframework.data spring-data-jpa @@ -264,6 +268,67 @@ test + + + io.dropwizard.metrics + metrics-core + ${metrics.version} + + + + + + io.dropwizard.metrics + metrics-logback + 3.2.2 + + + + io.dropwizard.metrics + metrics-jvm + ${metrics.version} + + + + + io.dropwizard.metrics + metrics-healthchecks + ${metrics.version} + + + + + io.dropwizard.metrics + metrics-graphite + ${metrics.version} + + + + + com.ryantenney.metrics + metrics-spring + 3.1.3 + + + + io.riemann + metrics3-riemann-reporter + 0.4.5 + + + + defunkt + logback-riemann-appender + 0.4.0 + + + + + + javax.inject + javax.inject + 1 + @@ -306,12 +378,12 @@ aspectj-maven-plugin 1.10 - + diff --git a/src/main/java/com/example/crud/ApplicationConfig.java b/src/main/java/com/example/crud/ApplicationConfiguration.java similarity index 52% rename from src/main/java/com/example/crud/ApplicationConfig.java rename to src/main/java/com/example/crud/ApplicationConfiguration.java index a616a5d..97c3c41 100644 --- a/src/main/java/com/example/crud/ApplicationConfig.java +++ b/src/main/java/com/example/crud/ApplicationConfiguration.java @@ -1,15 +1,14 @@ package com.example.crud; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Session; import com.example.crud.entities.AbstractAuditableEntity; import com.example.crud.entities.AbstractEntity; import com.example.crud.entities.Product; +import org.datanucleus.ExecutionContext; 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.datanucleus.store.StoreManager; +import org.datanucleus.store.connection.ManagedConnection; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @@ -22,18 +21,17 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.annotation.PostConstruct; +import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; -import java.net.UnknownHostException; @Configuration @EnableJpaRepositories @EnableJpaAuditing @EnableScheduling @EnableAspectJAutoProxy -@EnableCaching @EnableTransactionManagement -class ApplicationConfig { +class ApplicationConfiguration { @PostConstruct private void enhanceModelObjectBytecode() { @@ -60,43 +58,30 @@ class ApplicationConfig { return txManager; } - @Bean - public EmbeddedCacheManager cacheManager() { - return infinispanEmbeddedDistributedCacheManager(); - } - + // Auditing @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()); + // Cassandra + @Bean + public EntityManager entityManager() { + EntityManager em = entityManagerFactory().createEntityManager(); + return em; + } - 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 Session session() { + StoreManager storeManager = ((ExecutionContext)entityManager().getDelegate()).getNucleusContext().getStoreManager(); + ManagedConnection connection = storeManager.getConnection(-1); + Session session = (Session) connection.getConnection(); + return session; + } + + @Bean + public Cluster cluster() { + return session().getCluster(); } } diff --git a/src/main/java/com/example/crud/CacheClusterListener.java b/src/main/java/com/example/crud/CacheClusterListener.java deleted file mode 100644 index 5feeb5b..0000000 --- a/src/main/java/com/example/crud/CacheClusterListener.java +++ /dev/null @@ -1,52 +0,0 @@ -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/CacheConfiguration.java b/src/main/java/com/example/crud/CacheConfiguration.java new file mode 100644 index 0000000..66489ba --- /dev/null +++ b/src/main/java/com/example/crud/CacheConfiguration.java @@ -0,0 +1,135 @@ +package com.example.crud; + +import com.google.common.collect.Lists; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurer; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCache; +import org.springframework.cache.interceptor.CacheErrorHandler; +import org.springframework.cache.interceptor.CacheResolver; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.cache.support.CompositeCacheManager; +import org.springframework.cache.support.SimpleCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.stream.Collectors; + +@Configuration +//@ComponentScan("com.example.crud") +//@PropertySource("application.properties") +@EnableCaching +public class CacheConfiguration extends CachingConfigurerSupport { + + //private final CacheProperties cacheProperties; + + //@Autowired + //public CacheConfiguration(CacheProperties cacheProperties) { +// this.cacheProperties = cacheProperties; +// } + + @Qualifier("ehCacheCacheManager") + @Autowired(required = false) + private CacheManager ehCacheCacheManager; + + /* + @Qualifier("redisCacheManager") + @Autowired(required = false) + private CacheManager redisCacheManager; + */ + + @Bean + @Override + public CacheManager cacheManager() { +// if (cacheProperties.isEnabled()) { + + List cacheManagers = Lists.newArrayList(); + + if (this.ehCacheCacheManager != null) { + cacheManagers.add(this.ehCacheCacheManager); + } + + /* + if (this.redisCacheManager != null) { + cacheManagers.add(this.redisCacheManager); + } + */ + + CompositeCacheManager cacheManager = new CompositeCacheManager(); + + cacheManager.setCacheManagers(cacheManagers); + cacheManager.setFallbackToNoOpCache(false); + + return cacheManager; +/* + } else { + SimpleCacheManager cacheManager = new SimpleCacheManager(); + List caches = cacheProperties.getCacheNameList() + .stream() + .map(cacheName -> new ConcurrentMapCache(cacheName)) + .collect(Collectors.toList()); + cacheManager.setCaches(caches); + return cacheManager; + } +*/ + } + + @Bean + @Override + public CacheResolver cacheResolver() { + return null; + } + + @Bean + @Override + public KeyGenerator keyGenerator() { + /* Simplistic KeyGenerator example: + return new KeyGenerator() { + @Override + public Object generate(Object o, Method method, Object... params) { + StringBuilder sb = new StringBuilder(); + sb.append(o.getClass().getName()); + sb.append(method.getName()); + for (Object param : params) { + sb.append(param.toString()); + } + return sb.toString(); + } + }; + */ + // Same logic as the DefaultKeyGenerator + return new KeyGenerator() { + + public static final int NO_PARAM_KEY = 0; + public static final int NULL_PARAM_KEY = 53; + + public Object generate(Object target, Method method, Object... params) { + if (params.length == 1) { + return (params[0] == null ? NULL_PARAM_KEY : params[0]); + } + if (params.length == 0) { + return NO_PARAM_KEY; + } + int hashCode = 17; + for (Object object : params) { + hashCode = 31 * hashCode + (object == null ? NULL_PARAM_KEY : object.hashCode()); + } + return Integer.valueOf(hashCode); + } + }; + } + + @Bean + @Override + public CacheErrorHandler errorHandler () { + return null; + } + +} diff --git a/src/main/java/com/example/crud/EhCacheConfiguration.java b/src/main/java/com/example/crud/EhCacheConfiguration.java new file mode 100644 index 0000000..7fda79a --- /dev/null +++ b/src/main/java/com/example/crud/EhCacheConfiguration.java @@ -0,0 +1,26 @@ +package com.example.crud; + +import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.cache.ehcache.EhCacheCacheManager; + +@Configuration +public class EhCacheConfiguration { + + @Bean + public EhCacheCacheManager ehCacheCacheManager() { + return new EhCacheCacheManager(ehCacheManagerFactoryBean().getObject()); + } + + + @Bean + public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() { + EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean(); + cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml")); + cacheManagerFactoryBean.setShared(true); + return cacheManagerFactoryBean; + } + +} diff --git a/src/main/java/com/example/crud/JGroups.java b/src/main/java/com/example/crud/JGroups.java deleted file mode 100644 index a040a92..0000000 --- a/src/main/java/com/example/crud/JGroups.java +++ /dev/null @@ -1,236 +0,0 @@ -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 4641132..272e9a4 100644 --- a/src/main/java/com/example/crud/Main.java +++ b/src/main/java/com/example/crud/Main.java @@ -2,10 +2,11 @@ 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 com.example.crud.repositories.PersonRepository; +import org.datanucleus.api.jpa.JPAEntityManager; +import org.datanucleus.state.ObjectProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; @@ -21,66 +22,101 @@ import java.util.Map; */ 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[]) { - //cacheTest(); + Logger log = LoggerFactory.getLogger(Main.class);//getClass().getName()); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(ApplicationConfig.class); + ctx.scan("com.example"); + ctx.register(ApplicationConfiguration.class); ctx.refresh(); + // Enable MongoDB logging in general + System.setProperty("DEBUG.MONGO", "true"); + + // Enable DB operation tracing + System.setProperty("DB.TRACE", "true"); + // 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 + EntityManagerFactory cassandraEntityManagerFactory = Persistence.createEntityManagerFactory("crud"); + EntityManagerFactory mongoEntityManagerFactory = Persistence.createEntityManagerFactory("mongo"); // TODO: - // * types: int, bool, etc. - // * Set<> - // * L2/Caching via Infinispan (embedded, clustered) - // * MergingPersistenceUnitmanager - // * Draft/(Fluent)Builder Immutable Entites + // * LOCAL_QUORUM + // * compound primary keys + // * pillar for DDL + // * metrics + // * com.datastax.driver.core.Cluster.builder().withQueryOptions(‌​new QueryOptions().setConsistencyLevel(ConsistencyLevel.QUORUM)) + // * https://github.com/brndnmtthws/metrics-cassandra (c* as a sink for metrics) + // * https://github.com/addthis/metrics-reporter-config + + EntityManager em; + EntityTransaction tx; + JpaRepositoryFactory factory; + + //org.datanucleus.api.jpa.JPAEntityTransaction tx = (org.datanucleus.api.jpa.JPAEntityTransaction)pm.currentTransaction(); + //tx.setOption("transaction.isolation", 2); + + // Add a person to MongoDB + em = mongoEntityManagerFactory.createEntityManager(); + Person person; + /* + factory = new JpaRepositoryFactory(em); + PersonRepository repository = factory.getRepository(PersonRepository.class); + person = new Person(); + person.setPersonFirstName("James"); + person.setPersonLastName("Bond"); + person.setAge(42); + repository.save(person); + */ + tx = em.getTransaction(); + try { + tx.begin(); + person = new Person(); + person.setPersonFirstName("James"); + person.setPersonLastName("Bond"); + person.setAge(42); + em.merge(person); + + List objs = ((JPAEntityManager) em).getExecutionContext().getObjectsToBeFlushed(); + for (Object o : objs) { + log.debug("to be flushed: " + o.toString()); + } + tx.commit(); + } finally { + if (tx.isActive()) { + tx.rollback(); + } + em.close(); // This will detach all current managed objects + } // Persistence of a Product and a Book. - EntityManager em = emf.createEntityManager(); - EntityTransaction tx = em.getTransaction(); + em = cassandraEntityManagerFactory.createEntityManager(); + tx = em.getTransaction(); try { tx.begin(); Inventory inv = em.merge(new Inventory("My Inventory")); + inv.setDescription("This is my initial description."); Product product = new Product("Sony Discman", "A standard discman from Sony", 200.00); 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."); + Magazine magazine = new Magazine("Field and Stream", "A hunter's guide to the outdoors.", 3.29, "F&S, Inc.", + "23984729347", "F&S, Inc."); + + //product.setSeller(person); + //book.setSeller(person); + //magazine.setSeller(person); inv.addProduct(book); inv.addProduct(magazine); em.persist(inv); tx.commit(); -// 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); + log.error(">> Exception persisting data", e); System.err.println("Error persisting data : " + e.getMessage()); return; } finally { @@ -89,11 +125,11 @@ public class Main { } em.close(); } - emf.getCache().evictAll(); + cassandraEntityManagerFactory.getCache().evictAll(); System.out.println(""); // Perform a retrieve of the Inventory and detach it (by closing the EM) - em = emf.createEntityManager(); + em = cassandraEntityManagerFactory.createEntityManager(); tx = em.getTransaction(); Inventory inv = null; try { @@ -114,7 +150,7 @@ public class Main { tx.commit(); } catch (Exception e) { - NucleusLogger.GENERAL.error(">> Exception performing find() on data", e); + log.error(">> Exception performing find() on data", e); System.err.println("Error performing find() on data : " + e.getMessage()); return; } finally { @@ -128,17 +164,14 @@ public class Main { } System.out.println(""); - // Add a person to MongoDB - em = emf_mongo.createEntityManager(); + // Update a person to MongoDB + em = mongoEntityManagerFactory.createEntityManager(); tx = em.getTransaction(); - Person person; try { tx.begin(); - person = new Person(); - person.setPersonFirstName("James"); - person.setPersonLastName("Bond"); - person.setAge(42); - em.merge(person); + person = em.find(Person.class, person.getPersonId()); + person.setPersonLastName("Blunder"); + person.setAge(43); tx.commit(); } finally { if (tx.isActive()) { @@ -148,7 +181,7 @@ public class Main { } // Perform some query operations - em = emf.createEntityManager(); + em = cassandraEntityManagerFactory.createEntityManager(); tx = em.getTransaction(); try { tx.begin(); @@ -173,7 +206,7 @@ public class Main { tx.commit(); } catch (Exception e) { - NucleusLogger.GENERAL.error(">> Exception querying data", e); + log.error(">> Exception querying data", e); System.err.println("Error querying data : " + e.getMessage()); return; } finally { @@ -184,16 +217,54 @@ public class Main { } System.out.println(""); - em = emf.createEntityManager(); - JpaRepositoryFactory factory = new JpaRepositoryFactory(em); - InventoryRepository repository = factory.getRepository(InventoryRepository.class); - Inventory inventory = repository.findByName("My Inventory"); - System.out.println("SpringData/JPA: " + inventory.toString()); - em.close(); + em = cassandraEntityManagerFactory.createEntityManager(); + factory = new JpaRepositoryFactory(em); + tx = em.getTransaction(); + try { + tx.begin(); + InventoryRepository repository = factory.getRepository(InventoryRepository.class); + Inventory inventory = repository.findByName("My Inventory"); + System.out.println("SpringData/JPA: " + inventory.toString()); + inventory.setDescription("This is my updated description."); + tx.rollback(); + } + catch (Exception e) { + log.error(">> Exception in bulk delete of data", e); + System.err.println("Error in bulk delete of data : " + e.getMessage()); + return; + } finally { + if (tx.isActive()) { + tx.rollback(); + } + em.close(); + } + + em = cassandraEntityManagerFactory.createEntityManager(); + factory = new JpaRepositoryFactory(em); + tx = em.getTransaction(); + try { + tx.begin(); + InventoryRepository repository = factory.getRepository(InventoryRepository.class); + Inventory inventory = repository.findByName("My Inventory"); + inventory.setDescription("This is the final description."); + repository.save(inventory); + tx.commit(); + } + catch (Exception e) { + log.error(">> Exception in bulk delete of data", e); + System.err.println("Error in bulk delete of data : " + e.getMessage()); + return; + } finally { + if (tx.isActive()) { + tx.rollback(); + } + em.close(); + } + // Clean out the database - emf.getCache().evictAll(); - em = emf.createEntityManager(); + cassandraEntityManagerFactory.getCache().evictAll(); + em = cassandraEntityManagerFactory.createEntityManager(); tx = em.getTransaction(); try { tx.begin(); @@ -221,7 +292,7 @@ public class Main { tx.commit(); } catch (Exception e) { - NucleusLogger.GENERAL.error(">> Exception in bulk delete of data", e); + log.error(">> Exception in bulk delete of data", e); System.err.println("Error in bulk delete of data : " + e.getMessage()); return; } finally { @@ -233,6 +304,6 @@ public class Main { System.out.println(""); System.out.println("End of Tutorial"); - emf.close(); + cassandraEntityManagerFactory.close(); } } diff --git a/src/main/java/com/example/crud/entities/AbstractEntity.java b/src/main/java/com/example/crud/entities/AbstractEntity.java index 8fd9a1a..597638e 100644 --- a/src/main/java/com/example/crud/entities/AbstractEntity.java +++ b/src/main/java/com/example/crud/entities/AbstractEntity.java @@ -10,17 +10,23 @@ import java.io.Serializable; @MappedSuperclass public abstract class AbstractEntity { + @Version + protected long version; + + /* + // SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(inv); + // SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(product); + //System.out.println("Product and Book have been persisted, inventory: " + inv.getPrimaryKey().toString() + ", product: " + product.getPrimaryKey().toString()); + @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/Book.java b/src/main/java/com/example/crud/entities/Book.java index 5f824e6..affb696 100644 --- a/src/main/java/com/example/crud/entities/Book.java +++ b/src/main/java/com/example/crud/entities/Book.java @@ -29,6 +29,9 @@ public class Book extends Product @Basic private String publisher = null; + @Basic + private boolean paperback = false; + /** * Default Constructor. **/ diff --git a/src/main/java/com/example/crud/entities/Inventory.java b/src/main/java/com/example/crud/entities/Inventory.java index 8324f98..379b63d 100644 --- a/src/main/java/com/example/crud/entities/Inventory.java +++ b/src/main/java/com/example/crud/entities/Inventory.java @@ -20,6 +20,9 @@ public class Inventory extends AbstractAuditableEntity { @Id private String name=null; + @Basic + private String description; + @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH }, fetch = FetchType.EAGER) private Set products = new HashSet(); @@ -42,4 +45,7 @@ public class Inventory extends AbstractAuditableEntity { products.clear(); } + public void setDescription(String description) { + this.description = description; + } } diff --git a/src/main/java/com/example/crud/entities/Person.java b/src/main/java/com/example/crud/entities/Person.java index 37342f6..8655989 100644 --- a/src/main/java/com/example/crud/entities/Person.java +++ b/src/main/java/com/example/crud/entities/Person.java @@ -51,6 +51,13 @@ public class Person extends AbstractAuditableEntity { @OneToMany(mappedBy = "seller") private List products; + public Person() { + } + + public Person(String personId) { + this.personId = personId; + } + /** * Gets the person id. * diff --git a/src/main/java/com/example/crud/metrics/CoalescingReporter.java b/src/main/java/com/example/crud/metrics/CoalescingReporter.java new file mode 100644 index 0000000..dfbdc7a --- /dev/null +++ b/src/main/java/com/example/crud/metrics/CoalescingReporter.java @@ -0,0 +1,229 @@ +package com.example.crud.metrics; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricFilter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.ScheduledReporter; +import com.codahale.metrics.Snapshot; +import com.codahale.metrics.Timer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.Marker; + +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.concurrent.TimeUnit; + +/** + * A reporter class for logging metrics values to a {@link Logger} periodically, similar to + * {@link com.codahale.metrics.ConsoleReporter} or {@link com.codahale.metrics.CsvReporter}, but using + * the logging framework instead. It also supports specifying a {@link Marker} instance that can be used + * by custom appenders and filters for the bound logging toolkit to further process metrics reports. + */ +public final class CoalescingReporter extends ScheduledReporter { + + private final Logger logger; + private final Marker marker; + + /** + * Returns a new {@link Builder} for {@link CoalescingReporter}. + * + * @param registry the registry to report + * @return a {@link Builder} instance for a {@link CoalescingReporter} + */ + public static Builder forRegistry(MetricRegistry registry) { + return new Builder(registry); + } + + private CoalescingReporter(MetricRegistry registry, + Logger logger, + Marker marker, + TimeUnit rateUnit, + TimeUnit durationUnit, + MetricFilter filter) { + super(registry, "logger-reporter", filter, rateUnit, durationUnit); + this.logger = logger; + this.marker = marker; + } + + @Override + public void report(SortedMap gauges, + SortedMap counters, + SortedMap histograms, + SortedMap meters, + SortedMap timers) { + StringBuilder data = new StringBuilder(); + for (Entry entry : gauges.entrySet()) { + addGauge(data, entry.getKey(), entry.getValue()); + } + + for (Entry entry : counters.entrySet()) { + addCounter(data, entry.getKey(), entry.getValue()); + } + + for (Entry entry : histograms.entrySet()) { + addHistogram(data, entry.getKey(), entry.getValue()); + } + + for (Entry entry : meters.entrySet()) { + addMeter(data, entry.getKey(), entry.getValue()); + } + + for (Entry entry : timers.entrySet()) { + addTimer(data, entry.getKey(), entry.getValue()); + } + logger.info(marker, data.toString()); + } + + private void addTimer(StringBuilder data, String name, Timer timer) { + final Snapshot snapshot = timer.getSnapshot(); + data.append(" type=timer.").append(name).append(":"); + data.append(" count=").append(timer.getCount()); + data.append(", min=").append(convertDuration(snapshot.getMin())); + data.append(", max=").append(convertDuration(snapshot.getMax())); + data.append(", mean=").append(convertDuration(snapshot.getMean())); + data.append(", stdDev=").append(convertDuration(snapshot.getStdDev())); + data.append(", median=").append(convertDuration(snapshot.getMedian())); + data.append(", p75=").append(convertDuration(snapshot.get75thPercentile())); + data.append(", p95=").append(convertDuration(snapshot.get95thPercentile())); + data.append(", p98=").append(convertDuration(snapshot.get98thPercentile())); + data.append(", p99=").append(convertDuration(snapshot.get99thPercentile())); + data.append(", 999=").append(convertDuration(snapshot.get999thPercentile())); + data.append(", mean_rate=").append(convertRate(timer.getMeanRate())); + data.append(", m1=").append(convertRate(timer.getMeanRate())); + data.append(", m5=").append(convertRate(timer.getMeanRate())); + data.append(", m15=").append(convertRate(timer.getMeanRate())); + data.append(", rate_unit=").append(getRateUnit()); + data.append(", duration_unit=").append(getDurationUnit()); + } + + private void addMeter(StringBuilder data, String name, Meter meter) { + data.append(" type=meter.").append(name).append(":"); + data.append(" count=").append(meter.getCount()); + data.append(", mean_rate=").append(convertRate(meter.getMeanRate())); + data.append(", m1=").append(convertRate(meter.getOneMinuteRate())); + data.append(", m5=").append(convertRate(meter.getFiveMinuteRate())); + data.append(", m15=").append(convertRate(meter.getFifteenMinuteRate())); + data.append(", rate_unit=").append(getRateUnit()); + } + + private void addHistogram(StringBuilder data, String name, Histogram histogram) { + final Snapshot snapshot = histogram.getSnapshot(); + data.append(" type=histogram.").append(name).append(":"); + data.append(" count=").append(histogram.getCount()); + data.append(", min=").append(snapshot.getMin()); + data.append(", max=").append(snapshot.getMax()); + data.append(", mean=").append(snapshot.getMean()); + data.append(", stdDev=").append(snapshot.getStdDev()); + data.append(", median=").append(snapshot.getMedian()); + data.append(", p75=").append(snapshot.get75thPercentile()); + data.append(", p95=").append(snapshot.get95thPercentile()); + data.append(", p98=").append(snapshot.get98thPercentile()); + data.append(", p99=").append(snapshot.get99thPercentile()); + data.append(", 999=").append(snapshot.get999thPercentile()); + } + + private void addCounter(StringBuilder data, String name, Counter counter) { + data.append(" counter.").append(name).append(": ").append(counter.getCount()); + } + + private void addGauge(StringBuilder data, String name, Gauge gauge) { + data.append(" gauge.").append(name).append(": ").append(gauge.getValue()); + } + + @Override + protected String getRateUnit() { + return "events/" + super.getRateUnit(); + } + + /** + * A builder for {@link com.codahale.metrics.CsvReporter} instances. Defaults to logging to {@code metrics}, not + * using a marker, converting rates to events/second, converting durations to milliseconds, and + * not filtering metrics. + */ + public static final class Builder { + private final MetricRegistry registry; + private Logger logger; + private Marker marker; + private TimeUnit rateUnit; + private TimeUnit durationUnit; + private MetricFilter filter; + + private Builder(MetricRegistry registry) { + this.registry = registry; + this.logger = LoggerFactory.getLogger("metrics"); + this.marker = null; + this.rateUnit = TimeUnit.SECONDS; + this.durationUnit = TimeUnit.MILLISECONDS; + this.filter = MetricFilter.ALL; + } + + /** + * Log metrics to the given logger. + * + * @param logger a {@link Logger} + * @return {@code this} + */ + public Builder outputTo(Logger logger) { + this.logger = logger; + return this; + } + + /** + * Mark all logged metrics with the given marker. + * + * @param marker a {@link Marker} + * @return {@code this} + */ + public Builder markWith(Marker marker) { + this.marker = marker; + return this; + } + + /** + * Convert rates to the given time unit. + * + * @param rateUnit a unit of time + * @return {@code this} + */ + public Builder convertRatesTo(TimeUnit rateUnit) { + this.rateUnit = rateUnit; + return this; + } + + /** + * Convert durations to the given time unit. + * + * @param durationUnit a unit of time + * @return {@code this} + */ + public Builder convertDurationsTo(TimeUnit durationUnit) { + this.durationUnit = durationUnit; + return this; + } + + /** + * Only report metrics which match the given filter. + * + * @param filter a {@link MetricFilter} + * @return {@code this} + */ + public Builder filter(MetricFilter filter) { + this.filter = filter; + return this; + } + + /** + * Builds a {@link CoalescingReporter} with the given properties. + * + * @return a {@link CoalescingReporter} + */ + public CoalescingReporter build() { + return new CoalescingReporter(registry, logger, marker, rateUnit, durationUnit, filter); + } + } + +} diff --git a/src/main/java/com/example/crud/metrics/CoalescingReporterElementParser.java b/src/main/java/com/example/crud/metrics/CoalescingReporterElementParser.java new file mode 100644 index 0000000..fdf136d --- /dev/null +++ b/src/main/java/com/example/crud/metrics/CoalescingReporterElementParser.java @@ -0,0 +1,38 @@ +package com.example.crud.metrics; + +import com.ryantenney.metrics.spring.reporter.AbstractReporterElementParser; + +/** + * Reporter for metrics-spring which logs more compact, all in one line instead of one line for each metric. + */ +public class CoalescingReporterElementParser extends AbstractReporterElementParser { + + private static final String FILTER_REF = "filter-ref"; + private static final String FILTER_PATTERN = "filter"; + + @Override + public String getType() { + return "compact-slf4j"; + } + + @Override + protected Class getBeanClass() { + return CoalescingReporterFactoryBean.class; + } + + @Override + protected void validate(ValidationContext c) { + c.require(CoalescingReporterFactoryBean.PERIOD, DURATION_STRING_REGEX, "Period is required and must be in the form '\\d+(ns|us|ms|s|m|h|d)'"); + c.optional(CoalescingReporterFactoryBean.MARKER); + c.optional(CoalescingReporterFactoryBean.LOGGER); + c.optional(CoalescingReporterFactoryBean.RATE_UNIT, TIMEUNIT_STRING_REGEX, "Rate unit must be one of the enum constants from java.util.concurrent.TimeUnit"); + c.optional(CoalescingReporterFactoryBean.DURATION_UNIT, TIMEUNIT_STRING_REGEX, "Duration unit must be one of the enum constants from java.util.concurrent.TimeUnit"); + c.optional(FILTER_PATTERN); + c.optional(FILTER_REF); + if (c.has(FILTER_PATTERN) && c.has(FILTER_REF)) { + c.reject(FILTER_REF, "Reporter element must not specify both the 'filter' and 'filter-ref' attributes"); + } + c.rejectUnmatchedProperties(); + } + +} diff --git a/src/main/java/com/example/crud/metrics/CoalescingReporterFactoryBean.java b/src/main/java/com/example/crud/metrics/CoalescingReporterFactoryBean.java new file mode 100644 index 0000000..7d31e1a --- /dev/null +++ b/src/main/java/com/example/crud/metrics/CoalescingReporterFactoryBean.java @@ -0,0 +1,54 @@ +package com.example.crud.metrics; + +import com.ryantenney.metrics.spring.reporter.AbstractScheduledReporterFactoryBean; +import org.slf4j.LoggerFactory; +import org.slf4j.MarkerFactory; + +import java.util.concurrent.TimeUnit; + +/** + * CoalescingReporterFactoryBean. + */ +public class CoalescingReporterFactoryBean extends AbstractScheduledReporterFactoryBean { + + /** Period attribute. */ + public static final String PERIOD = "period"; + /** Duration unit. */ + public static final String DURATION_UNIT = "duration-unit"; + /** Rate unit. */ + public static final String RATE_UNIT = "rate-unit"; + /** Marker. */ + public static final String MARKER = "marker"; + /** Logger. */ + public static final String LOGGER = "logger"; + + @Override + public Class getObjectType() { + return CoalescingReporter.class; + } + + @Override + protected CoalescingReporter createInstance() { + final CoalescingReporter.Builder reporter = CoalescingReporter.forRegistry(getMetricRegistry()); + if (hasProperty(DURATION_UNIT)) { + reporter.convertDurationsTo(getProperty(DURATION_UNIT, TimeUnit.class)); + } + if (hasProperty(RATE_UNIT)) { + reporter.convertRatesTo(getProperty(RATE_UNIT, TimeUnit.class)); + } + reporter.filter(getMetricFilter()); + if (hasProperty(MARKER)) { + reporter.markWith(MarkerFactory.getMarker(getProperty(MARKER))); + } + if (hasProperty(LOGGER)) { + reporter.outputTo(LoggerFactory.getLogger(getProperty(LOGGER))); + } + return reporter.build(); + } + + @Override + protected long getPeriod() { + return convertDurationString(getProperty(PERIOD)); + } + +} diff --git a/src/main/java/com/example/crud/metrics/MetricsConfiguration.java b/src/main/java/com/example/crud/metrics/MetricsConfiguration.java new file mode 100644 index 0000000..c73e013 --- /dev/null +++ b/src/main/java/com/example/crud/metrics/MetricsConfiguration.java @@ -0,0 +1,151 @@ +package com.example.crud.metrics; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.util.ContextInitializer; +import ch.qos.logback.core.joran.spi.JoranException; +import com.codahale.metrics.*; +import com.codahale.metrics.health.HealthCheckRegistry; +import com.codahale.metrics.jvm.*; +import com.codahale.metrics.logback.InstrumentedAppender; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Session; +import com.ryantenney.metrics.spring.config.annotation.EnableMetrics; +import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter; +import org.datanucleus.store.StoreManager; +import org.datanucleus.store.connection.ManagedConnection; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import javax.persistence.EntityManagerFactory; +import java.lang.management.ManagementFactory; +import java.util.concurrent.TimeUnit; + +@Configuration +@EnableMetrics(proxyTargetClass = true) +public class MetricsConfiguration extends MetricsConfigurerAdapter { + + private static final String PROP_METRIC_REG_JVM_MEMORY = "jvm.memory"; + private static final String PROP_METRIC_REG_JVM_GARBAGE = "jvm.garbage"; + private static final String PROP_METRIC_REG_JVM_THREADS = "jvm.threads"; + private static final String PROP_METRIC_REG_JVM_FILES = "jvm.files"; + private static final String PROP_METRIC_REG_JVM_BUFFERS = "jvm.buffers"; + + private final Logger log = (Logger)LoggerFactory.getLogger(getClass().getName()); + + private MetricRegistry metricRegistry = new MetricRegistry(); + + private HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry(); + + @Autowired + ApplicationContext context; + + @Override + @Bean + public MetricRegistry getMetricRegistry() { + return metricRegistry; + } + + @Override + @Bean + public HealthCheckRegistry getHealthCheckRegistry() { + return healthCheckRegistry; + } + + @PostConstruct + public void init() { +/* + final LoggerContext factory = (LoggerContext) LoggerFactory.getILoggerFactory(); + final Logger root = factory.getLogger("console");//Logger.ROOT_LOGGER_NAME); + + final InstrumentedAppender metrics = new InstrumentedAppender(); + metrics.setContext(root.getLoggerContext()); + metrics.start(); + root.addAppender(metrics); +*/ + + LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory(); + loggerContext.reset(); + ContextInitializer initializer = new ContextInitializer(loggerContext); + try { + initializer.autoConfig(); + } catch (JoranException e) { + e.printStackTrace(); + } + + + log.debug("Registering JVM gauges"); + metricRegistry.registerAll(new OperatingSystemGaugeSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_MEMORY, new MemoryUsageGaugeSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_GARBAGE, new GarbageCollectorMetricSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_THREADS, new ThreadStatesGaugeSet()); + metricRegistry.register(PROP_METRIC_REG_JVM_FILES, new FileDescriptorRatioGauge()); + metricRegistry.register(PROP_METRIC_REG_JVM_BUFFERS, + new BufferPoolMetricSet(ManagementFactory.getPlatformMBeanServer())); + + Cluster cluster = context.getBean(Cluster.class); + cluster.getMetrics().getRegistry().addListener( + new com.codahale.metrics.MetricRegistryListener() { + + private final String METRIC_NAME_PREFIX = "com.datastax."; + + @Override + public void onGaugeAdded(String name, Gauge gauge) { + //if (metricRegistry.getNames().contains(name)) { + // name is already taken, maybe prefix with a namespace + //} else { + metricRegistry.register(METRIC_NAME_PREFIX + name, gauge); + } + + @Override + public void onGaugeRemoved(String name) { + } + + @Override + public void onCounterAdded(String name, Counter counter) { + metricRegistry.register(METRIC_NAME_PREFIX + name, counter); + } + + @Override + public void onCounterRemoved(String name) { + } + + @Override + public void onHistogramAdded(String name, Histogram histogram) { + metricRegistry.register(METRIC_NAME_PREFIX + name, histogram); + } + + @Override + public void onHistogramRemoved(String name) { + } + + @Override + public void onMeterAdded(String name, Meter meter) { + metricRegistry.register(METRIC_NAME_PREFIX + name, meter); + } + + @Override + public void onMeterRemoved(String name) { + } + + @Override + public void onTimerAdded(String name, Timer timer) { + metricRegistry.register(METRIC_NAME_PREFIX + name, timer); + } + + @Override + public void onTimerRemoved(String name) { + } + }); + CoalescingReporter reporter = CoalescingReporter.forRegistry(metricRegistry) + .convertRatesTo(TimeUnit.SECONDS) + .convertDurationsTo(TimeUnit.MILLISECONDS) + .build(); + reporter.start(5, TimeUnit.SECONDS); + } + +} diff --git a/src/main/java/com/example/crud/metrics/OperatingSystemGaugeSet.java b/src/main/java/com/example/crud/metrics/OperatingSystemGaugeSet.java new file mode 100644 index 0000000..8149876 --- /dev/null +++ b/src/main/java/com/example/crud/metrics/OperatingSystemGaugeSet.java @@ -0,0 +1,125 @@ +package com.example.crud.metrics; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricSet; + +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * A set of gauges for operating system settings. + */ +public class OperatingSystemGaugeSet implements MetricSet { + + private final OperatingSystemMXBean mxBean; + private final Optional committedVirtualMemorySize; + private final Optional totalSwapSpaceSize; + private final Optional freeSwapSpaceSize; + private final Optional processCpuTime; + private final Optional freePhysicalMemorySize; + private final Optional totalPhysicalMemorySize; + private final Optional openFileDescriptorCount; + private final Optional maxFileDescriptorCount; + private final Optional systemCpuLoad; + private final Optional processCpuLoad; + + /** + * Creates new gauges using the platform OS bean. + */ + public OperatingSystemGaugeSet() { + this(ManagementFactory.getOperatingSystemMXBean()); + } + + /** + * Creates a new gauges using the given OS bean. + * + * @param mxBean an {@link OperatingSystemMXBean} + */ + public OperatingSystemGaugeSet(OperatingSystemMXBean mxBean) { + this.mxBean = mxBean; + + committedVirtualMemorySize = getMethod("getCommittedVirtualMemorySize"); + totalSwapSpaceSize = getMethod("getTotalSwapSpaceSize"); + freeSwapSpaceSize = getMethod("getFreeSwapSpaceSize"); + processCpuTime = getMethod("getProcessCpuTime"); + freePhysicalMemorySize = getMethod("getFreePhysicalMemorySize"); + totalPhysicalMemorySize = getMethod("getTotalPhysicalMemorySize"); + openFileDescriptorCount = getMethod("getOpenFileDescriptorCount"); + maxFileDescriptorCount = getMethod("getMaxFileDescriptorCount"); + systemCpuLoad = getMethod("getSystemCpuLoad"); + processCpuLoad = getMethod("getProcessCpuLoad"); + } + + + @Override + public Map getMetrics() { + final Map gauges = new HashMap<>(); + + gauges.put("committedVirtualMemorySize", (Gauge) () -> invokeLong(committedVirtualMemorySize)); + gauges.put("totalSwapSpaceSize", (Gauge) () -> invokeLong(totalSwapSpaceSize)); + gauges.put("freeSwapSpaceSize", (Gauge) () -> invokeLong(freeSwapSpaceSize)); + gauges.put("processCpuTime", (Gauge) () -> invokeLong(processCpuTime)); + gauges.put("freePhysicalMemorySize", (Gauge) () -> invokeLong(freePhysicalMemorySize)); + gauges.put("totalPhysicalMemorySize", (Gauge) () -> invokeLong(totalPhysicalMemorySize)); + gauges.put("fd.usage", (Gauge) () -> invokeRatio(openFileDescriptorCount, maxFileDescriptorCount)); + gauges.put("systemCpuLoad", (Gauge) () -> invokeDouble(systemCpuLoad)); + gauges.put("processCpuLoad", (Gauge) () -> invokeDouble(processCpuLoad)); + + return gauges; + } + + private Optional getMethod(String name) { + try { + final Method method = mxBean.getClass().getDeclaredMethod(name); + method.setAccessible(true); + return Optional.of(method); + } catch (NoSuchMethodException e) { + return Optional.empty(); + } + } + + private long invokeLong(Optional method) { + if (method.isPresent()) { + try { + return (long) method.get().invoke(mxBean); + } catch (IllegalAccessException | InvocationTargetException ite) { + return 0L; + } + } + return 0L; + } + + private double invokeDouble(Optional method) { + if (method.isPresent()) { + try { + return (double) method.get().invoke(mxBean); + } catch (IllegalAccessException | InvocationTargetException ite) { + return 0.0; + } + } + return 0.0; + } + + private double invokeRatio(Optional numeratorMethod, Optional denominatorMethod) { + if (numeratorMethod.isPresent() && denominatorMethod.isPresent()) { + try { + long numerator = (long) numeratorMethod.get().invoke(mxBean); + long denominator = (long) denominatorMethod.get().invoke(mxBean); + if (0 == denominator) { + return Double.NaN; + } + return 1.0 * numerator / denominator; + } catch (IllegalAccessException | InvocationTargetException ite) { + return Double.NaN; + } + } + return Double.NaN; + } + +} diff --git a/src/main/java/com/example/crud/metrics/SpringConfiguringClass.java b/src/main/java/com/example/crud/metrics/SpringConfiguringClass.java new file mode 100644 index 0000000..ff84ff4 --- /dev/null +++ b/src/main/java/com/example/crud/metrics/SpringConfiguringClass.java @@ -0,0 +1,22 @@ +package com.example.crud.metrics; + +import java.util.concurrent.TimeUnit; +import org.springframework.context.annotation.Configuration; +import com.codahale.metrics.ConsoleReporter; +import com.codahale.metrics.MetricRegistry; +import com.ryantenney.metrics.spring.config.annotation.EnableMetrics; +import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter; + +@Configuration +@EnableMetrics +public class SpringConfiguringClass extends MetricsConfigurerAdapter { + + @Override + public void configureReporters(MetricRegistry metricRegistry) { + // registerReporter allows the MetricsConfigurerAdapter to + // shut down the reporter when the Spring context is closed + registerReporter(ConsoleReporter.forRegistry(metricRegistry).build()) + .start(1, TimeUnit.MINUTES); + } + +} diff --git a/src/main/java/com/example/crud/repositories/InventoryRepository.java b/src/main/java/com/example/crud/repositories/InventoryRepository.java index ecaa720..9f429a4 100644 --- a/src/main/java/com/example/crud/repositories/InventoryRepository.java +++ b/src/main/java/com/example/crud/repositories/InventoryRepository.java @@ -1,8 +1,6 @@ package com.example.crud.repositories; -import com.codahale.metrics.annotation.Metered; import com.example.crud.entities.Inventory; -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; @@ -12,6 +10,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; +import java.beans.Transient; import java.util.List; //@Metrics(registry = "${this.registry}") @@ -31,4 +30,7 @@ public interface InventoryRepository extends JpaRepository, J @CacheEvict(value = "inventory", key = "#name") void deleteInventoryBy(String name); + @Transactional + //CacheEvict(value = "inventory", key = "#name") + S save(S s); } diff --git a/src/main/java/com/example/crud/repositories/PersonRepository.java b/src/main/java/com/example/crud/repositories/PersonRepository.java new file mode 100644 index 0000000..384aa33 --- /dev/null +++ b/src/main/java/com/example/crud/repositories/PersonRepository.java @@ -0,0 +1,8 @@ +package com.example.crud.repositories; + +import com.example.crud.entities.Inventory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface PersonRepository extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/src/main/resources/META-INF/orm.xml b/src/main/resources/META-INF/orm.xml index 24589d7..cc57e2d 100644 --- a/src/main/resources/META-INF/orm.xml +++ b/src/main/resources/META-INF/orm.xml @@ -46,6 +46,7 @@ + @@ -63,6 +64,9 @@ + + + diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml index 6036c4f..ff4b3fc 100644 --- a/src/main/resources/META-INF/persistence.xml +++ b/src/main/resources/META-INF/persistence.xml @@ -15,7 +15,7 @@ - + com.example.crud.entities.Inventory com.example.crud.entities.Product com.example.crud.entities.Book @@ -25,10 +25,12 @@ - - - + + + + + diff --git a/src/main/resources/ehcache.xml b/src/main/resources/ehcache.xml index 1c9f9db..6dbcb6f 100644 --- a/src/main/resources/ehcache.xml +++ b/src/main/resources/ehcache.xml @@ -1,13 +1,21 @@ - - - - - - + + + + + + + + diff --git a/src/main/resources/infinispan.xml b/src/main/resources/infinispan.xml deleted file mode 100644 index 083bdfa..0000000 --- a/src/main/resources/infinispan.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index f8a5427..03e1e30 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -17,92 +17,58 @@ - - - - - + + 10000 + 0 - + - - - - - - - - - - - + - - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - - - + - + + + + + + + + + + + + + + +