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}