I hava an application that uses embedded jetty and works fine.
I'm having a problem with the temporary directories generated by jetty.
When an application is deployed it generates a temporary directory with the extracted war (jetty-0.0.0.0-8888-app.war-_app-any-4643933123854766078.dir), all good.
But when i remove/replace this war with another version (same context), it create another temporary directory and the other one is not completely deleted. I see jetty removes all css, html files, but the web-inf/lib/*.jar are no removed.
The webAppContext.setPersistTempDirectory
is set to false, i already checked.
I already tried to change the temp directory that jetty uses too.
Here's my WebAppProvider:
WebAppProvider webAppProvider = new WebAppProvider();
webAppProvider.setMonitoredDirName("webapps");
webAppProvider.setScanInterval(1);
webAppProvider.setExtractWars(true);
webAppProvider.setDefaultsDescriptor("webdefault.xml");
webAppProvider.setTempDir(new File("work"));
String webDefault = Thread.currentThread().getContextClassLoader().getResource("webdefault.xml").getPath();
if (webDefault != null) {
File f = new File(webDefault);
if (f.exists() && !f.isDirectory()) {
webAppProvider.setDefaultsDescriptor(webDefault);
}
}
Here's the DeploymentManager:
DeploymentManager deploymentManager = new DeploymentManager();
deploymentManager.setContexts(contextHandlerCollection);
deploymentManager.setContextAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/org.apache.taglibs.taglibs-standard-impl-.*\\.jar$");
deploymentManager.addAppProvider(webAppProvider);
The server setup:
server = new Server(8888);
server.setStopAtShutdown(true);
server.setStopTimeout(5000);
handlerCollection.addHandler(contextHandlerCollection);
handlerCollection.addHandler(new DefaultHandler());
server.setHandler(handlerCollection);
server.addBean(deployManager);
I noticed that i only can manually delete the directory when i stop jetty. I think thats the only way it releases the process that uses the jars and let me completely remove.
Obs: I don't want to have to stop the server so i can remove the folders.
Thanks in advance.
Well, find a solution. Just posting here to help anybody in need.
Here goes my files:
DeploymentManager:
DeploymentManager deploymentManager = new DeploymentManager();
deploymentManager.setContexts(contextHandlerCollection);
deploymentManager.setContextAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/org.apache.taglibs.taglibs-standard-impl-.*\\.jar$");
deploymentManager.addLifeCycleBinding(new StandardStarter());
deploymentManager.addLifeCycleBinding(new StandardStopperCustom());
deploymentManager.addLifeCycleBinding(new StandardDeployer());
deploymentManager.addLifeCycleBinding(new StandardUndeployer());
deploymentManager.setUseStandardBindings(false);
deploymentManager.addAppProvider(webAppProvider);
WebAppProvider:
WebAppProviderCustom webAppProvider = new WebAppProviderCustom();
webAppProvider.setMonitoredDirName("webapps");
webAppProvider.setScanInterval(1);
webAppProvider.setExtractWars(true);
webAppProvider.setDefaultsDescriptor("webdefault.xml");
webAppProvider.setTempDir(new File("work"));
String webDefault = Thread.currentThread().getContextClassLoader().getResource("webdefault.xml").getPath();
if (webDefault != null) {
File f = new File(webDefault);
if (f.exists() && !f.isDirectory()) {
webAppProvider.setDefaultsDescriptor(webDefault);
}
}
StopperCustom:
public class StandardStopperCustom extends StandardStopper {
@Override
public void processBinding(Node node, App app) throws Exception {
ContextHandler handler;
try {
handler = app.getContextHandler();
if (handler instanceof WebAppContext) {
WebAppContext webapp = (WebAppContext) handler;
File tempDirectory = (File) webapp.getAttribute("javax.servlet.context.tempdir");
String baseDirectory = tempDirectory.getName();
if (webapp.getClassLoader() instanceof WebAppClassLoaderCustom && !baseDirectory.isEmpty()) {
WebAppClassLoaderCustom classLoader = (WebAppClassLoaderCustom) webapp.getClassLoader();
super.processBinding(node, app);
classLoader.close();
cleanUpTempDirectory(baseDirectory);
}
} else {
super.processBinding(node, app);
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
public void cleanUpTempDirectory(String tempDirectory) {
File work = new File(Constantes.workDirectory);
File[] jettyFolders = work.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (name.equalsIgnoreCase(tempDirectory)) {
return true;
}
return false;
}
});
if (jettyFolders != null && jettyFolders.length > 0) {
for (File file : jettyFolders) {
IO.delete(file);
}
}
}
}
Set a custom classloader to the webappcontext: webAppContext.setClassLoader(new WebAppClassLoaderCustom(webAppContext));
WebAppClassLoaderCustom:
public class WebAppClassLoaderCustom extends WebAppClassLoader {
private HashSet<String> jarFileToClose = new HashSet<String>();
public WebAppClassLoaderCustom(Context context) throws IOException {
super(context);
}
public WebAppClassLoaderCustom(ClassLoader parent, Context context) throws IOException {
super(parent, context);
}
@Override
public void close() {
System.out.println("Closing ClassLoader.");
jarFileToClose.clear();
closeClassLoader(this);
// finalizeNativeLibs(this);
cleanupJarFileFactory();
try {
super.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@SuppressWarnings("rawtypes")
private boolean closeClassLoader(ClassLoader cl) {
boolean res = false;
if (cl == null) {
return res;
}
Class classURLClassLoader = URLClassLoader.class;
Field f = null;
try {
f = classURLClassLoader.getDeclaredField("ucp");
} catch (NoSuchFieldException e1) {
// ignore
}
if (f != null) {
f.setAccessible(true);
Object obj = null;
try {
obj = f.get(cl);
} catch (IllegalAccessException e1) {
// ignore
}
if (obj != null) {
final Object ucp = obj;
f = null;
try {
f = ucp.getClass().getDeclaredField("loaders");
} catch (NoSuchFieldException e1) {
// ignore
}
if (f != null) {
f.setAccessible(true);
ArrayList loaders = null;
try {
loaders = (ArrayList) f.get(ucp);
res = true;
} catch (IllegalAccessException e1) {
// ignore
}
for (int i = 0; loaders != null && i < loaders.size(); i++) {
obj = loaders.get(i);
f = null;
try {
f = obj.getClass().getDeclaredField("jar");
} catch (NoSuchFieldException e) {
// ignore
}
if (f != null) {
f.setAccessible(true);
try {
obj = f.get(obj);
} catch (IllegalAccessException e1) {
// ignore
}
if (obj instanceof JarFile) {
final JarFile jarFile = (JarFile) obj;
jarFileToClose.add(jarFile.getName());
try {
jarFile.close();
} catch (IOException e) {
// ignore
}
}
}
}
}
}
}
return res;
}
@SuppressWarnings("rawtypes")
private boolean finalizeNativeLibs(ClassLoader cl) {
boolean res = false;
Class classClassLoader = ClassLoader.class;
java.lang.reflect.Field nativeLibraries = null;
try {
nativeLibraries = classClassLoader.getDeclaredField("nativeLibraries");
} catch (NoSuchFieldException e1) {
// ignore
}
if (nativeLibraries == null) {
return res;
}
nativeLibraries.setAccessible(true);
Object obj = null;
try {
obj = nativeLibraries.get(cl);
} catch (IllegalAccessException e1) {
// ignore
}
if (!(obj instanceof Vector)) {
return res;
}
res = true;
Vector java_lang_ClassLoader_NativeLibrary = (Vector) obj;
for (Object lib : java_lang_ClassLoader_NativeLibrary) {
java.lang.reflect.Method finalize = null;
try {
finalize = lib.getClass().getDeclaredMethod("finalize", new Class[0]);
} catch (NoSuchMethodException e) {
// ignore
}
if (finalize != null) {
finalize.setAccessible(true);
try {
finalize.invoke(lib, new Object[0]);
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
// ignore
}
}
}
return res;
}
@SuppressWarnings("rawtypes")
private boolean cleanupJarFileFactory() {
boolean res = false;
Class classJarURLConnection = null;
try {
classJarURLConnection = Class.forName("sun.net.www.protocol.jar.JarURLConnection");
} catch (ClassNotFoundException e) {
// ignore
}
if (classJarURLConnection == null) {
return res;
}
Field f = null;
try {
f = classJarURLConnection.getDeclaredField("factory");
} catch (NoSuchFieldException e) {
// ignore
}
if (f == null) {
return res;
}
f.setAccessible(true);
Object obj = null;
try {
obj = f.get(null);
} catch (IllegalAccessException e) {
// ignore
}
if (obj == null) {
return res;
}
Class classJarFileFactory = obj.getClass();
//
HashMap fileCache = null;
try {
f = classJarFileFactory.getDeclaredField("fileCache");
f.setAccessible(true);
obj = f.get(null);
if (obj instanceof HashMap) {
fileCache = (HashMap) obj;
}
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
// ignore
}
HashMap urlCache = null;
try {
f = classJarFileFactory.getDeclaredField("urlCache");
f.setAccessible(true);
obj = f.get(null);
if (obj instanceof HashMap) {
urlCache = (HashMap) obj;
}
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
// ignore
}
if (urlCache != null) {
HashMap urlCacheTmp = (HashMap) urlCache.clone();
Iterator it = urlCacheTmp.keySet().iterator();
while (it.hasNext()) {
obj = it.next();
if (!(obj instanceof JarFile)) {
continue;
}
JarFile jarFile = (JarFile) obj;
if (jarFileToClose.contains(jarFile.getName())) {
try {
jarFile.close();
} catch (IOException e) {
// ignore
}
if (fileCache != null) {
fileCache.remove(urlCache.get(jarFile));
}
urlCache.remove(jarFile);
}
}
res = true;
} else if (fileCache != null) {
// urlCache := null
HashMap fileCacheTmp = (HashMap) fileCache.clone();
Iterator it = fileCacheTmp.keySet().iterator();
while (it.hasNext()) {
Object key = it.next();
obj = fileCache.get(key);
if (!(obj instanceof JarFile)) {
continue;
}
JarFile jarFile = (JarFile) obj;
if (jarFileToClose.contains(jarFile.getName())) {
try {
jarFile.close();
} catch (IOException e) {
// ignore
}
fileCache.remove(key);
}
}
res = true;
}
jarFileToClose.clear();
return res;
}
}