嵌入式Jetty无法完全清除临时目录(保留lib / * .jar文件)

4
我有一个使用嵌入式Jetty的应用程序,运行良好。
我遇到了Jetty生成的临时目录的问题。当应用程序部署时,它会生成一个带有提取的war文件的临时目录(jetty-0.0.0.0-8888-app.war-_app-any-4643933123854766078.dir),一切正常。但是当我删除/替换这个war文件并使用另一个版本(相同的上下文)时,它会创建另一个临时目录,而另一个目录没有完全被删除。我看到Jetty删除了所有的css、html文件,但是web-inf/lib/*.jar没有被删除。
webAppContext.setPersistTempDirectory已经设置为false,我已经检查过了。
我已经尝试过更改Jetty使用的临时目录。
这是我的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);
    }
}

这里是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);

服务器设置:

server = new Server(8888);
server.setStopAtShutdown(true);
server.setStopTimeout(5000);

handlerCollection.addHandler(contextHandlerCollection);
handlerCollection.addHandler(new DefaultHandler());

server.setHandler(handlerCollection);
server.addBean(deployManager);

我注意到只有在停止Jetty时才能手动删除目录。我认为这是唯一的方法,它会释放使用JAR文件的进程,让我完全删除它们。
注意:我不想停止服务器就可以删除文件夹。
提前致谢。

它是一个纯Java应用程序还是使用了框架(例如Spring)?当您部署新版本时,如何停止应用程序? - guido
好的,我认为你可以扩展org.eclipse.jetty.deploy.bindings.StandardStoppero.e.j.d.b.StandarUndeployer并将其传递给DeploymentManager.addLifeCycleBinding(AppLifeCycle.Binding binding) - guido
@Guido 谢谢,我会尝试并告诉你结果。 - pecci
也许从StandardDeployer开始;我猜默认的undeployer无法删除旧的jar文件,可能与某些类加载器相关的问题有关。 - guido
好的,谢谢...我会告诉你的。 - pecci
显示剩余9条评论
1个回答

5

我找到了一个解决方案,并在此发布以帮助有需要的人。

以下是我的文件:

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);
            }
        }
    }
}

将自定义类加载器设置到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;
    }

}

这个在 Git 仓库里面有吗? - Dinesh
很遗憾,不行。抱歉。 - pecci
好的...那么如果我按Ctrl+C或关闭服务器,它也会清理临时文件吗?(我猜不会。) - Dinesh
1
我已经很久没有做这件事了,但如果我记得没错,杀死它不会清理临时文件。 - pecci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接