001package com.nimbusds.common.servlet; 002 003 004import java.io.InputStream; 005import java.util.Date; 006import java.util.concurrent.atomic.AtomicBoolean; 007import javax.servlet.ServletContext; 008import javax.servlet.ServletContextEvent; 009import javax.servlet.ServletContextListener; 010 011import org.apache.commons.lang3.StringUtils; 012 013import org.apache.logging.log4j.LogManager; 014import org.apache.logging.log4j.Logger; 015 016import org.infinispan.manager.DefaultCacheManager; 017import org.infinispan.manager.EmbeddedCacheManager; 018import org.infinispan.notifications.Listener; 019import org.infinispan.notifications.cachemanagerlistener.annotation.CacheStarted; 020import org.infinispan.notifications.cachemanagerlistener.annotation.CacheStopped; 021import org.infinispan.notifications.cachemanagerlistener.event.CacheStartedEvent; 022import org.infinispan.notifications.cachemanagerlistener.event.CacheStoppedEvent; 023 024 025/** 026 * Configures and launches an Infinispan cache manager at servlet context 027 * startup. The cache manager is stopped at servlet context shutdown (if 028 * previously launched). 029 * 030 * <p>The name / path of the Infinispan configuration file is specified in a 031 * servlet context init parameter {@code infinispan.configurationFile}. 032 * 033 * <p>The launched Infinispan cache manager will be exported to the servlet 034 * context as an {@code org.infinispan.manager.EmbeddedCacheManager}, under a 035 * key that bears the interface name. 036 */ 037public class InfinispanLauncher implements ServletContextListener { 038 039 040 /** 041 * The name of the Infinispan configuration filename. 042 */ 043 public static final String INFINISPAN_CONFIG_FILENAME = "infinispan.configurationFile"; 044 045 046 /** 047 * The name of the servlet context attribute for the launched 048 * Infinispan cache manager. 049 */ 050 public static final String INFINISPAN_CTX_ATTRIBUTE_NAME = "org.infinispan.manager.EmbeddedCacheManager"; 051 052 053 /** 054 * Logs Infinispan status and network topology after startup. 055 * Thread-safe. 056 */ 057 @Listener 058 public static class StartLogger { 059 060 061 /** 062 * Startup initiation timestamp. 063 */ 064 private final Date initiationTimestamp; 065 066 067 /** 068 * Cluster info logged status. 069 */ 070 private final AtomicBoolean clusterInfoLogged = new AtomicBoolean(false); 071 072 073 /** 074 * Creates a new Infinispan startup logger. 075 * 076 * @param initiationTimestamp The startup initiation timestamp. 077 * Must not be {@code null}. 078 */ 079 public StartLogger(final Date initiationTimestamp) { 080 this.initiationTimestamp = initiationTimestamp; 081 } 082 083 084 @CacheStarted 085 public void logCacheManagerStart(final CacheStartedEvent event) { 086 087 Logger log = LogManager.getLogger("MAIN"); 088 089 EmbeddedCacheManager cacheManager = event.getCacheManager(); 090 091 if (!cacheManager.getCacheManagerConfiguration().isClustered()) { 092 // Local Infinispan (no cluster) 093 log.info("[CM8008] Infinispan clustering: not configured"); 094 } else if (clusterInfoLogged.compareAndSet(false, true)) { 095 // Clustered Infinispan, log cluster conf for the first start cache event only 096 // to prevent duplicate log lines with same cluster info 097 log.info("[CM8007] Infinispan status: {}", cacheManager.getStatus()); 098 log.info("[CM8009] Infinispan cluster name: {}", cacheManager.getCacheManagerConfiguration().transport().clusterName()); 099 log.info("[CM8010] Infinispan cluster local node logical address: {}", cacheManager.getAddress()); 100 log.info("[CM8019] Infinispan cluster local node physical address(es): {}", cacheManager.getCacheManagerConfiguration().transport().transport().getPhysicalAddresses()); 101 log.info("[CM8011] Infinispan cluster coordinator logical address: {}", cacheManager.getCoordinator()); 102 log.info("[CM8016] Infinispan cluster local node is coordinator: {}", cacheManager.isCoordinator()); 103 log.info("[CM8012] Infinispan cluster members: {}", cacheManager.getMembers()); 104 log.info("[CM8013] Infinispan cluster distributed sync timeout: {}", cacheManager.getCacheManagerConfiguration().transport().distributedSyncTimeout()); 105 log.info("[CM8014] Infinispan cluster JGroups configuration file: {}", cacheManager.getCacheManagerConfiguration().transport().properties().getProperty("configurationFile")); 106 } 107 108 final Date now = new Date(); 109 log.info("[CM8006] Started Infinispan cache {} in {} ms", event.getCacheName(), now.getTime() - initiationTimestamp.getTime()); 110 } 111 } 112 113 114 /** 115 * Logs Infinispan stop. Thread-safe. 116 */ 117 @Listener 118 public static class StopLogger { 119 120 121 /** 122 * Shutdown initiation timestamp. 123 */ 124 private final Date initiationTimestamp; 125 126 127 /** 128 * Creates a new Infinispan stop logger. 129 * 130 * @param initiationTimestamp The shutdown initiation 131 * timestamp. Must not be 132 * {@code null}. 133 */ 134 public StopLogger(Date initiationTimestamp) { 135 this.initiationTimestamp = initiationTimestamp; 136 } 137 138 139 @CacheStopped 140 public void logCacheManagerStop(final CacheStoppedEvent event) { 141 142 final Date now = new Date(); 143 final long duration = now.getTime() - initiationTimestamp.getTime(); 144 String msg = "[CM8015] Stopped Infinispan cache " + event.getCacheName() + " in " + duration + " ms"; 145 System.out.println(msg); // Backup logging 146 LogManager.getLogger("MAIN").info(msg); 147 } 148 } 149 150 151 /** 152 * Reference to the cache manager. 153 */ 154 private EmbeddedCacheManager cacheManager; 155 156 157 /** 158 * Handler for servlet context startup events; configures and launches 159 * an Infinispan cache manager using the configuration file specified 160 * in the servlet context parameter {@code infinispan.configurationFile}. 161 * 162 * <p>The configuration file location must be relative to the web 163 * application directory, e.g. {@code /WEB-INF/infinispan.xml}. 164 * 165 * <p>Exceptions are logged at ERROR level using Log4j. 166 * 167 * @param sce A servlet context event. 168 */ 169 @Override 170 public void contextInitialized(final ServletContextEvent sce) { 171 172 Logger log = LogManager.getLogger("MAIN"); 173 174 ServletContext servletContext = sce.getServletContext(); 175 176 String configFile = servletContext.getInitParameter(INFINISPAN_CONFIG_FILENAME); 177 178 LogManager.getLogger("MAIN").info("[CM8000] Starting Infinispan..."); 179 180 if (StringUtils.isBlank(configFile)) { 181 182 String msg = "Couldn't load Infinispan configuration: Missing servlet context parameter \"" + INFINISPAN_CONFIG_FILENAME + "\""; 183 log.error("[CM8001] " + msg); 184 throw new RuntimeException(msg); 185 } 186 187 log.info("[CM8002] Infinispan configuration file: {}", configFile); 188 189 InputStream is = servletContext.getResourceAsStream(configFile); 190 191 if (is == null) { 192 String msg = "Couldn't load Infinispan configuration file: " + configFile; 193 log.error("[CM8003] " + msg); 194 throw new RuntimeException(msg); 195 } 196 197 try { 198 cacheManager = new DefaultCacheManager(is); 199 200 } catch (Exception e) { 201 202 String msg = "Couldn't start Infinispan cache manager: " + e.getMessage(); 203 log.error("[CM8004] " + msg); 204 throw new RuntimeException(msg, e); 205 } 206 207 cacheManager.addListener(new StartLogger(new Date())); 208 209 servletContext.setAttribute(INFINISPAN_CTX_ATTRIBUTE_NAME, cacheManager); 210 } 211 212 213 /** 214 * Handler for servlet context shutdown events. Stops the Infinispan 215 * cache manager. 216 * 217 * @param sce A servlet context event. 218 */ 219 @Override 220 public void contextDestroyed(final ServletContextEvent sce) { 221 222 Logger log = LogManager.getLogger("MAIN"); 223 224 if (cacheManager == null) { 225 String msg = "[CM8017] Infinispan cache manager not initialised"; 226 log.error(msg); 227 throw new IllegalStateException(msg); 228 } 229 230 String msg = "[CM8005] Stopping Infinispan..."; 231 System.out.println(msg); // Backup logging 232 log.info(msg); 233 234 cacheManager.addListener(new StopLogger(new Date())); 235 236 cacheManager.stop(); 237 } 238}