Skip to content

Commit

Permalink
统一日志框架为 slf4j, 日志实现为 logback. log4j 和 commons-logging 转调回 slf4j.
Browse files Browse the repository at this point in the history
上下文关闭时反注册 mysql 驱动
  • Loading branch information
Fireply committed May 29, 2016
1 parent c63f168 commit 0c42acd
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 12 deletions.
5 changes: 5 additions & 0 deletions WebRoot/WEB-INF/web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>Enter</display-name>

<!-- 解决 多应用发布时 Tomcat 内存溢出的问题 -->
<!-- <listener>
<listener-class>org.fireply.enter.listener.ContextFinalizer</listener-class>
</listener> -->

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Expand Down
29 changes: 17 additions & 12 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,35 +31,40 @@ task wrapper(type: Wrapper) {
gretty {
servletContainer = 'tomcat8'
httpPort = 9080
// springBoot = true
scanInterval = 0
}

repositories {
mavenCentral()
}

configurations {
all*.exclude module: 'commons-logging'
all*.exclude module: 'log4j'
}

dependencies {
providedCompile 'javax.servlet:servlet-api:2.5'
runtime 'javax.servlet:jstl:1.2'

compile('org.apache.struts:struts2-core:2.3.28.1') {
exclude module:'javassist'
}
compile('org.apache.struts:struts2-spring-plugin:2.3.28.1') {
exclude module:'javassist'
}
compile 'org.springframework:spring-context:4.2.6.RELEASE'
compile 'org.springframework:spring-web:4.2.6.RELEASE'
compile 'org.springframework:spring-webmvc:4.2.6.RELEASE'
compile 'org.springframework:spring-jdbc:4.2.6.RELEASE'
compile 'org.springframework:spring-orm:4.2.6.RELEASE'
compile 'org.springframework:spring-tx:4.2.6.RELEASE'
compile 'org.springframework:spring-test:4.2.6.RELEASE'
compile 'org.hibernate:hibernate-core:4.3.11.Final'
compile('org.apache.struts:struts2-core:2.3.28.1') {
exclude module:'javassist'
}
compile('org.apache.struts:struts2-spring-plugin:2.3.28.1') {
exclude module:'javassist'
}

compile 'mysql:mysql-connector-java:5.1.38'

compile 'org.apache.logging.log4j:log4j-core:2.5'
compile 'org.slf4j:slf4j-api:1.7.12'
compile 'ch.qos.logback:logback-core:1.1.3'
compile 'ch.qos.logback:logback-classic:1.1.3'
compile 'org.slf4j:log4j-over-slf4j:1.7.12'
compile 'org.slf4j:jcl-over-slf4j:1.7.12'

testCompile 'junit:junit:4.12'
}
Expand Down
183 changes: 183 additions & 0 deletions src/main/java/org/fireply/enter/listener/ContextFinalizer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package org.fireply.enter.listener;

import java.lang.reflect.Method;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* Context finalization to close threads (MySQL memory leak prevention).
* This solution combines the best techniques described in the linked Stack
* Overflow answer.
* @see <a href="https://stackoverflow.com/questions/11872316/tomcat-guice-jdbc-memory-leak">Tomcat Guice/JDBC Memory Leak</a>
*/
public class ContextFinalizer
implements ServletContextListener {

private static final Logger LOGGER =
LoggerFactory.getLogger(ContextFinalizer.class);

/**
* Information for cleaning up a thread.
*/
private class ThreadInfo {

/**
* Name of the thread's initiating class.
*/
private final String name;

/**
* Cue identifying the thread.
*/
private final String cue;

/**
* Name of the method to stop the thread.
*/
private final String stop;

/**
* Basic constructor.
* @param n Name of the thread's initiating class.
* @param c Cue identifying the thread.
* @param s Name of the method to stop the thread.
*/
ThreadInfo(final String n, final String c, final String s) {
this.name = n;
this.cue = c;
this.stop = s;
}

/**
* @return the name
*/
public String getName() {
return this.name;
}

/**
* @return the cue
*/
public String getCue() {
return this.cue;
}

/**
* @return the stop
*/
public String getStop() {
return this.stop;
}
}

/**
* List of information on threads required to stop. This list may be
* expanded as necessary.
*/
private List<ThreadInfo> threads = Arrays.asList(
// Special cleanup for MySQL JDBC Connector.
new ThreadInfo(
"com.mysql.jdbc.AbandonedConnectionCleanupThread", //$NON-NLS-1$
"Abandoned connection cleanup thread", //$NON-NLS-1$
"shutdown" //$NON-NLS-1$
),
new ThreadInfo(
"sun.security.provider.SeedGenerator", //$NON-NLS-1$
"SeedGenerator Thread", //$NON-NLS-1$
"no method to stop it" //$NON-NLS-1$
)
);

@Override
public void contextInitialized(final ServletContextEvent sce) {
LOGGER.debug("After context initialized");
}

@Override
public final void contextDestroyed(final ServletContextEvent sce) {

LOGGER.debug("After context destroyed");

// Deregister all drivers.
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver d = drivers.nextElement();
try {
if(d.getClass().getClassLoader() == cl) {
DriverManager.deregisterDriver(d);
LOGGER.info("Driver {} deregistered", d);
}
else {
LOGGER.info("Driver {} 没有执行反注册,因为它可能在其他地方使用着", d);
}
} catch (SQLException e) {
LOGGER.warn(
String.format(
"Failed to deregister driver %s", //$NON-NLS-1$
d
),
e
);
}
}

// Handle remaining threads.
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for (Thread t:threadArray) {
for (ThreadInfo i:this.threads) {
if (t.getName().contains(i.getCue())) {
synchronized (t) {
try {
Class<?> cls = Class.forName(i.getName());
if (cls != null) {
Method mth = cls.getMethod(i.getStop());
if (mth != null) {
mth.invoke(null);
LOGGER.info(
String.format(
"Connection cleanup thread %s shutdown successfully.", //$NON-NLS-1$
i.getName()
)
);
}
}
} catch (NoSuchMethodException e) {
t.interrupt();
try {
t.join();
} catch (InterruptedException ie) {
LOGGER.warn("停止线程 {} 失败", i.getName());
ie.printStackTrace();
}
LOGGER.info("停止线程 {} 成功", i.getName());
} catch (Throwable thr) {
LOGGER.warn(
String.format(
"Failed to shutdown connection cleanup thread %s: ", //$NON-NLS-1$
i.getName(),
thr.getMessage()
)
);
thr.printStackTrace();
}
}
}
}
}
}

}
21 changes: 21 additions & 0 deletions src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender" debug="true" scan="true" scanPeriod="15 seconds">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<logger name="org.springframework" level="info" />

<logger name="org.hibernate" level="info" />

<logger name="org.apache.struts2" level="info" />

<logger name="com.opensymphony.xwork2" level="info" />

<root level="debug">
<appender-ref ref="console"/>
</root>

</configuration>

0 comments on commit 0c42acd

Please sign in to comment.