1
18 package net.bull.javamelody;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.security.CodeSource;
23 import java.sql.Driver;
24 import java.sql.DriverManager;
25 import java.sql.SQLException;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Set;
31 import java.util.Timer;
32 import java.util.TimerTask;
33 import java.util.regex.Pattern;
34
35 import javax.management.JMException;
36 import javax.management.MBeanServer;
37 import javax.management.ObjectName;
38
39 import net.bull.javamelody.internal.common.I18N;
40 import net.bull.javamelody.internal.common.LOG;
41 import net.bull.javamelody.internal.common.Parameters;
42 import net.bull.javamelody.internal.model.Collector;
43 import net.bull.javamelody.internal.model.Counter;
44 import net.bull.javamelody.internal.model.JRobin;
45 import net.bull.javamelody.internal.model.JobInformations;
46 import net.bull.javamelody.internal.model.MBeans;
47 import net.bull.javamelody.internal.model.MavenArtifact;
48 import net.bull.javamelody.internal.model.Period;
49 import net.bull.javamelody.internal.model.SamplingProfiler;
50 import net.bull.javamelody.internal.model.UpdateChecker;
51 import net.bull.javamelody.internal.web.HttpCookieManager;
52 import net.bull.javamelody.internal.web.MailReport;
53 import net.bull.javamelody.internal.web.MonitoringController;
54
55
59 class FilterContext {
60 private static final boolean MOJARRA_AVAILABLE = isMojarraAvailable();
61 private static final boolean JPA2_AVAILABLE = isJpa2Available();
62
63 private final String applicationType;
64 private final Collector collector;
65 private final Timer timer;
66 private final SamplingProfiler samplingProfiler;
67 private final TimerTask collectTimerTask;
68 private final Set<ObjectName> jmxNames = new HashSet<>();
69
70 private static final class CollectTimerTask extends TimerTask {
71 private final Collector collector;
72
73 CollectTimerTask(Collector collector) {
74 super();
75 this.collector = collector;
76 }
77
78
79 @Override
80 public void run() {
81
82 collector.collectLocalContextWithoutErrors();
83 }
84 }
85
86 FilterContext(final String applicationType) {
87 super();
88 assert applicationType != null;
89 this.applicationType = applicationType;
90
91 boolean initOk = false;
92 this.timer = new Timer("javamelody"
93 + Parameters.getContextPath(Parameters.getServletContext()).replace('/', ' '),
94 true);
95 try {
96 logSystemInformationsAndParameters();
97
98 initLogs();
99
100 if (Parameter.CONTEXT_FACTORY_ENABLED.getValueAsBoolean()) {
101 MonitoringInitialContextFactory.init();
102 }
103
104
105
106
107
108
109 JdbcWrapper.SINGLETON.initServletContext(Parameters.getServletContext());
110 if (!Parameters.isNoDatabase()) {
111 JdbcWrapper.SINGLETON.rebindDataSources();
112 } else {
113
114
115 JdbcWrapper.SINGLETON.stop();
116 }
117
118
119 if (JobInformations.QUARTZ_AVAILABLE) {
120 JobGlobalListener.initJobGlobalListener();
121 }
122
123 if (MOJARRA_AVAILABLE) {
124 JsfActionHelper.initJsfActionListener();
125 }
126 if (JPA2_AVAILABLE) {
127 JpaPersistence.initPersistenceProviderResolver();
128 }
129
130 this.samplingProfiler = initSamplingProfiler();
131
132 final List<Counter> counters = initCounters();
133 final String application = Parameters.getCurrentApplication();
134 this.collector = new Collector(application, counters, this.samplingProfiler);
135 this.collectTimerTask = new CollectTimerTask(collector);
136
137 initCollect();
138
139 if (Parameter.JMX_EXPOSE_ENABLED.getValueAsBoolean()) {
140 initJmxExpose();
141 }
142
143 UpdateChecker.init(timer, collector, applicationType);
144
145 if (Parameters.getServletContext().getServerInfo().contains("Google App Engine")) {
146
147 final String fontConfig = System.getProperty("java.home")
148 + "/lib/fontconfig.Prodimage.properties";
149 if (new File(fontConfig).exists()) {
150 System.setProperty("sun.awt.fontconfig", fontConfig);
151 }
152 }
153
154 initOk = true;
155 } finally {
156 if (!initOk) {
157
158
159 timer.cancel();
160 LOG.debug("JavaMelody init failed");
161 }
162 }
163 }
164
165 private static List<Counter> initCounters() {
166
167 final Counter sqlCounter = JdbcWrapper.SINGLETON.getSqlCounter();
168 final Counter httpCounter = new Counter(Counter.HTTP_COUNTER_NAME, "dbweb.png", sqlCounter);
169 final Counter errorCounter = new Counter(Counter.ERROR_COUNTER_NAME, "error.png");
170 errorCounter.setMaxRequestsCount(250);
171
172 final Counter jpaCounter = MonitoringProxy.getJpaCounter();
173 final Counter ejbCounter = MonitoringProxy.getEjbCounter();
174 final Counter springCounter = MonitoringProxy.getSpringCounter();
175 final Counter guiceCounter = MonitoringProxy.getGuiceCounter();
176 final Counter servicesCounter = MonitoringProxy.getServicesCounter();
177 final Counter strutsCounter = MonitoringProxy.getStrutsCounter();
178 final Counter jsfCounter = MonitoringProxy.getJsfCounter();
179 final Counter logCounter = LoggingHandler.getLogCounter();
180 final Counter jspCounter = JspWrapper.getJspCounter();
181 final List<Counter> counters;
182 if (JobInformations.QUARTZ_AVAILABLE) {
183 final Counter jobCounter = JobGlobalListener.getJobCounter();
184 counters = Arrays.asList(httpCounter, sqlCounter, jpaCounter, ejbCounter, springCounter,
185 guiceCounter, servicesCounter, strutsCounter, jsfCounter, jspCounter,
186 errorCounter, logCounter, jobCounter);
187 } else {
188 counters = Arrays.asList(httpCounter, sqlCounter, jpaCounter, ejbCounter, springCounter,
189 guiceCounter, servicesCounter, strutsCounter, jsfCounter, jspCounter,
190 errorCounter, logCounter);
191 }
192
193 setRequestTransformPatterns(counters);
194 final String displayedCounters = Parameter.DISPLAYED_COUNTERS.getValue();
195 if (displayedCounters == null) {
196
197 httpCounter.setDisplayed(true);
198 sqlCounter.setDisplayed(!Parameters.isNoDatabase());
199 errorCounter.setDisplayed(true);
200 logCounter.setDisplayed(true);
201 jpaCounter.setDisplayed(jpaCounter.isUsed());
202 ejbCounter.setDisplayed(ejbCounter.isUsed());
203 springCounter.setDisplayed(springCounter.isUsed());
204 guiceCounter.setDisplayed(guiceCounter.isUsed());
205 servicesCounter.setDisplayed(servicesCounter.isUsed());
206 strutsCounter.setDisplayed(strutsCounter.isUsed());
207 jsfCounter.setDisplayed(jsfCounter.isUsed());
208 jspCounter.setDisplayed(jspCounter.isUsed());
209 } else {
210 setDisplayedCounters(counters, displayedCounters);
211 }
212 LOG.debug("counters initialized");
213 return counters;
214 }
215
216 private static void setRequestTransformPatterns(List<Counter> counters) {
217 for (final Counter counter : counters) {
218
219 final Parameter parameter = Parameter
220 .valueOfIgnoreCase(counter.getName() + "_TRANSFORM_PATTERN");
221 if (parameter.getValue() != null) {
222 final Pattern pattern = Pattern.compile(parameter.getValue(),
223 Pattern.MULTILINE | Pattern.DOTALL);
224 counter.setRequestTransformPattern(pattern);
225 }
226 }
227 }
228
229 private static void setDisplayedCounters(List<Counter> counters, String displayedCounters) {
230 for (final Counter counter : counters) {
231
232
233 counter.setDisplayed(counter.isJobCounter());
234 }
235 if (!displayedCounters.isEmpty()) {
236 for (final String displayedCounter : displayedCounters.split(",")) {
237 final String displayedCounterName = displayedCounter.trim();
238 boolean found = false;
239 for (final Counter counter : counters) {
240 if (displayedCounterName.equalsIgnoreCase(counter.getName())) {
241 counter.setDisplayed(true);
242 found = true;
243 break;
244 }
245 }
246 if (!found) {
247 throw new IllegalArgumentException("Unknown counter: " + displayedCounterName);
248 }
249 }
250 }
251 }
252
253 private void initCollect() {
254 try {
255 Class.forName("org.jrobin.core.RrdDb");
256
257
258 Class.forName("org.jrobin.core.RrdException");
259 } catch (final ClassNotFoundException e) {
260 LOG.debug("jrobin classes unavailable: collect of data is disabled");
261 HttpCookieManager.setDefaultRange(Period.TOUT.getRange());
262
263 return;
264 }
265
266 if (collector.isStorageUsedByMultipleInstances()) {
267 LOG.info(I18N.getString("storage_used_by_multiple_instances"));
268 }
269
270 try {
271 JRobin.initBackendFactory(timer);
272 } catch (final IOException e) {
273 LOG.warn(e.toString(), e);
274 }
275 final int resolutionSeconds = Parameters.getResolutionSeconds();
276 final int periodMillis = resolutionSeconds * 1000;
277
278 timer.schedule(collectTimerTask, periodMillis, periodMillis);
279 LOG.debug("collect task scheduled every " + resolutionSeconds + 's');
280
281
282
283 collector.collectLocalContextWithoutErrors();
284 LOG.debug("first collect of data done");
285
286 if (Parameter.MAIL_SESSION.getValue() != null
287 && Parameter.ADMIN_EMAILS.getValue() != null) {
288 MailReport.scheduleReportMailForLocalServer(collector, timer);
289 LOG.debug("mail reports scheduled for " + Parameter.ADMIN_EMAILS.getValue());
290 }
291 }
292
293 private SamplingProfiler initSamplingProfiler() {
294 if (Parameter.SAMPLING_SECONDS.getValue() != null) {
295 final SamplingProfiler sampler;
296 final String excludedPackagesParameter = Parameter.SAMPLING_EXCLUDED_PACKAGES
297 .getValue();
298 final String includedPackagesParameter = Parameter.SAMPLING_INCLUDED_PACKAGES
299 .getValue();
300 if (excludedPackagesParameter == null && includedPackagesParameter == null) {
301 sampler = new SamplingProfiler();
302 } else {
303 sampler = new SamplingProfiler(excludedPackagesParameter,
304 includedPackagesParameter);
305 }
306 final TimerTask samplingTimerTask = new TimerTask() {
307 @Override
308 public void run() {
309 sampler.update();
310 }
311 };
312 final long periodInMillis = Math
313 .round(Double.parseDouble(Parameter.SAMPLING_SECONDS.getValue()) * 1000);
314 this.timer.schedule(samplingTimerTask, 10000, periodInMillis);
315 LOG.debug("hotspots sampling initialized");
316
317 return sampler;
318 }
319 return null;
320 }
321
322 private static void initLogs() {
323
324 LoggingHandler.getSingleton().register();
325
326 if (LOG.LOG4J_ENABLED) {
327
328 Log4JAppender.getSingleton().register();
329 }
330
331 if (LOG.LOG4J2_ENABLED) {
332
333 Log4J2Appender.getSingleton().register();
334 }
335
336 if (LOG.LOGBACK_ENABLED) {
337
338 LogbackAppender.getSingleton().register();
339 }
340 LOG.debug("log listeners initialized");
341 }
342
343 private static boolean isMojarraAvailable() {
344 try {
345 Class.forName("com.sun.faces.application.ActionListenerImpl");
346 return true;
347 } catch (final Throwable e) {
348 return false;
349 }
350 }
351
352 private static boolean isJpa2Available() {
353 try {
354 Class.forName("javax.persistence.spi.PersistenceProviderResolverHolder");
355 return true;
356 } catch (final Throwable e) {
357 return false;
358 }
359 }
360
361 private void logSystemInformationsAndParameters() {
362
363 LOG.debug("OS: " + System.getProperty("os.name") + ' '
364 + System.getProperty("sun.os.patch.level") + ", " + System.getProperty("os.arch")
365 + '/' + System.getProperty("sun.arch.data.model"));
366 LOG.debug("Java: " + System.getProperty("java.runtime.name") + ", "
367 + System.getProperty("java.runtime.version"));
368 LOG.debug("Server: " + Parameters.getServletContext().getServerInfo());
369 LOG.debug("Webapp context: " + Parameters.getContextPath(Parameters.getServletContext()));
370 LOG.debug("JavaMelody version: " + Parameters.JAVAMELODY_VERSION);
371 final String location = getJavaMelodyLocation();
372 if (location != null) {
373 LOG.debug("JavaMelody classes loaded from: " + location);
374 }
375 LOG.debug("Application type: " + applicationType);
376 LOG.debug("Application version: " + MavenArtifact.getWebappVersion());
377 LOG.debug("Host: " + Parameters.getHostName() + '@' + Parameters.getHostAddress());
378 for (final Parameter parameter : Parameter.values()) {
379 final String value = parameter.getValue();
380 if (value != null && parameter != Parameter.ANALYTICS_ID) {
381 if (parameter == Parameter.AUTHORIZED_USERS) {
382 LOG.debug("parameter defined: " + Parameter.AUTHORIZED_USERS.getCode()
383 + "=*****");
384 } else {
385 LOG.debug("parameter defined: " + parameter.getCode() + '=' + value);
386 }
387 }
388 }
389 }
390
391 private static String getJavaMelodyLocation() {
392 final Class<FilterContext> clazz = FilterContext.class;
393 final CodeSource codeSource = clazz.getProtectionDomain().getCodeSource();
394 if (codeSource != null && codeSource.getLocation() != null) {
395 String location = codeSource.getLocation().toString();
396
397
398 final String clazzFileName = clazz.getSimpleName() + ".class";
399 if (location.endsWith(clazzFileName)) {
400 location = location.substring(0, location.length() - clazzFileName.length());
401 }
402 return location;
403 }
404 return null;
405 }
406
407
412 private void initJmxExpose() {
413 final String packageName = getClass().getName().substring(0,
414 getClass().getName().length() - getClass().getSimpleName().length() - 1);
415 String webapp = Parameters.getContextPath(Parameters.getServletContext());
416 if (webapp.length() >= 1 && webapp.charAt(0) == '/') {
417 webapp = webapp.substring(1);
418 }
419 final List<Counter> counters = collector.getCounters();
420 final MBeanServer platformMBeanServer = MBeans.getPlatformMBeanServer();
421 try {
422 for (final Counter counter : counters) {
423 if (!Parameters.isCounterHidden(counter.getName())) {
424 final CounterRequestMXBean.CounterRequestMXBeanImpl mxBean = new CounterRequestMXBean.CounterRequestMXBeanImpl(
425 counter);
426 final ObjectName name = new ObjectName(
427 packageName + ":type=CounterRequest,context=" + webapp + ",name="
428 + counter.getName());
429 platformMBeanServer.registerMBean(mxBean, name);
430 jmxNames.add(name);
431 }
432 }
433 LOG.debug("JMX mbeans registered");
434 } catch (final JMException e) {
435 LOG.warn("failed to register JMX mbeans", e);
436 }
437 }
438
439 void stopCollector() {
440
441 if (collectTimerTask != null) {
442
443 collectTimerTask.cancel();
444 }
445
446 collector.stop();
447 }
448
449 void destroy() {
450 try {
451 try {
452 if (collector != null) {
453 new MonitoringController(collector, null).writeHtmlToLastShutdownFile();
454 }
455 } finally {
456
457 JdbcWrapper.SINGLETON.stop();
458
459 deregisterJdbcDriver();
460
461
462 deregisterLogs();
463
464
465 if (JobInformations.QUARTZ_AVAILABLE) {
466 JobGlobalListener.destroyJobGlobalListener();
467 }
468
469 unregisterJmxExpose();
470 }
471 } finally {
472 MonitoringInitialContextFactory.stop();
473
474
475
476
477 if (timer != null) {
478 timer.cancel();
479 }
480 if (samplingProfiler != null) {
481 samplingProfiler.clear();
482 }
483 if (collector != null) {
484 collector.stop();
485 }
486 Collector.stopJRobin();
487 Collector.detachVirtualMachine();
488 }
489 }
490
491 private static void deregisterJdbcDriver() {
492
493
494 final Class<FilterContext> classe = FilterContext.class;
495 final String packageName = classe.getName().substring(0,
496 classe.getName().length() - classe.getSimpleName().length() - 1);
497 for (final Driver driver : Collections.list(DriverManager.getDrivers())) {
498 if (driver.getClass().getName().startsWith(packageName)) {
499 try {
500 DriverManager.deregisterDriver(driver);
501 } catch (final SQLException e) {
502
503 throw new IllegalStateException(e);
504 }
505 }
506 }
507 }
508
509 private static void deregisterLogs() {
510 if (LOG.LOGBACK_ENABLED) {
511 LogbackAppender.getSingleton().deregister();
512 }
513 if (LOG.LOG4J_ENABLED) {
514 Log4JAppender.getSingleton().deregister();
515 }
516 LoggingHandler.getSingleton().deregister();
517 }
518
519
522 private void unregisterJmxExpose() {
523 if (jmxNames.isEmpty()) {
524 return;
525 }
526 try {
527 final MBeanServer platformMBeanServer = MBeans.getPlatformMBeanServer();
528 for (final ObjectName name : jmxNames) {
529 platformMBeanServer.unregisterMBean(name);
530 }
531 } catch (final JMException e) {
532 LOG.warn("failed to unregister JMX beans", e);
533 }
534 }
535
536 Collector getCollector() {
537 return collector;
538 }
539
540 Timer getTimer() {
541 return timer;
542 }
543 }
544