ARTEMIS-1058 Jars in web tmp dir locked on Windows
Because Sun's URLClassLoader never closes it's jar file handles Jetty doesn't cleanup is temp web dir after Artemis broker shut down normally on Windows. To work around this a new process is forked before broker VM exits to clean up the tmp dir if they are not deleted. The new process out lives the main VM so that those jar's handles are released and the temp dir can be cleaned up.
This commit is contained in:
parent
02dcc9e37b
commit
ae00423034
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.activemq.artemis.boot;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to remove the jar files
|
||||||
|
* in temp web dir on Windows platform where
|
||||||
|
* handles of the jar files are never released
|
||||||
|
* by URLClassLoader until the whole VM exits.
|
||||||
|
*/
|
||||||
|
public class WebTmpCleaner {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
String[] filesToClean = args[0].split(",");
|
||||||
|
|
||||||
|
//It needs to retry a bit as we are not sure
|
||||||
|
//when the main VM exists.
|
||||||
|
boolean allCleaned = false;
|
||||||
|
int maxRetries = 100;
|
||||||
|
while (!allCleaned && maxRetries-- > 0) {
|
||||||
|
allCleaned = true;
|
||||||
|
for (String f : filesToClean) {
|
||||||
|
if (!f.trim().isEmpty()) {
|
||||||
|
File file = new File(f);
|
||||||
|
if (file.exists()) {
|
||||||
|
deleteFile(file);
|
||||||
|
allCleaned = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Thread.sleep(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final void deleteFile(final File file) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
String[] files = file.list();
|
||||||
|
for (String path : files) {
|
||||||
|
File f = new File(file, path);
|
||||||
|
deleteFile(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -31,7 +31,6 @@ import org.apache.activemq.artemis.dto.AppDTO;
|
||||||
import org.apache.activemq.artemis.dto.ComponentDTO;
|
import org.apache.activemq.artemis.dto.ComponentDTO;
|
||||||
import org.apache.activemq.artemis.dto.WebServerDTO;
|
import org.apache.activemq.artemis.dto.WebServerDTO;
|
||||||
import org.apache.activemq.artemis.utils.FileUtil;
|
import org.apache.activemq.artemis.utils.FileUtil;
|
||||||
import org.apache.activemq.artemis.utils.TimeUtils;
|
|
||||||
import org.eclipse.jetty.server.ConnectionFactory;
|
import org.eclipse.jetty.server.ConnectionFactory;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
|
@ -61,10 +60,12 @@ public class WebServerComponent implements ExternalComponent {
|
||||||
private String consoleUrl;
|
private String consoleUrl;
|
||||||
private List<WebAppContext> webContexts;
|
private List<WebAppContext> webContexts;
|
||||||
private ServerConnector connector;
|
private ServerConnector connector;
|
||||||
|
private String artemisHome;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(ComponentDTO config, String artemisInstance, String artemisHome) throws Exception {
|
public void configure(ComponentDTO config, String artemisInstance, String artemisHome) throws Exception {
|
||||||
webServerConfig = (WebServerDTO) config;
|
webServerConfig = (WebServerDTO) config;
|
||||||
|
this.artemisHome = artemisHome;
|
||||||
uri = new URI(webServerConfig.bind);
|
uri = new URI(webServerConfig.bind);
|
||||||
server = new Server();
|
server = new Server();
|
||||||
String scheme = uri.getScheme();
|
String scheme = uri.getScheme();
|
||||||
|
@ -237,28 +238,38 @@ public class WebServerComponent implements ExternalComponent {
|
||||||
public void internalStop() throws Exception {
|
public void internalStop() throws Exception {
|
||||||
server.stop();
|
server.stop();
|
||||||
if (webContexts != null) {
|
if (webContexts != null) {
|
||||||
|
|
||||||
File tmpdir = null;
|
File tmpdir = null;
|
||||||
|
StringBuilder strBuilder = new StringBuilder();
|
||||||
|
boolean found = false;
|
||||||
for (WebAppContext context : webContexts) {
|
for (WebAppContext context : webContexts) {
|
||||||
tmpdir = context.getTempDirectory();
|
tmpdir = context.getTempDirectory();
|
||||||
|
|
||||||
if (tmpdir != null && !context.isPersistTempDirectory()) {
|
if (tmpdir != null && tmpdir.exists() && !context.isPersistTempDirectory()) {
|
||||||
//tmpdir will be removed by deleteOnExit()
|
//tmpdir will be removed by deleteOnExit()
|
||||||
//somehow when broker is stopped and restarted quickly
|
//However because the URLClassLoader never release/close its opened
|
||||||
//this tmpdir won't get deleted sometimes
|
//jars the jar file won't be able to get deleted on Windows platform
|
||||||
boolean fileDeleted = TimeUtils.waitOnBoolean(false, 5000, tmpdir::exists);
|
//until after the process fully terminated. To fix this here arranges
|
||||||
|
//a separate process to try clean up the temp dir
|
||||||
if (!fileDeleted) {
|
|
||||||
//because the execution order of shutdown hooks are
|
|
||||||
//not determined, so it's possible that the deleteOnExit
|
|
||||||
//is executed after this hook, in that case we force a delete.
|
|
||||||
FileUtil.deleteDirectory(tmpdir);
|
FileUtil.deleteDirectory(tmpdir);
|
||||||
logger.debug("Force to delete temporary file on shutdown: " + tmpdir.getAbsolutePath());
|
|
||||||
if (tmpdir.exists()) {
|
if (tmpdir.exists()) {
|
||||||
ActiveMQWebLogger.LOGGER.tmpFileNotDeleted(tmpdir);
|
ActiveMQWebLogger.LOGGER.tmpFileNotDeleted(tmpdir);
|
||||||
|
strBuilder.append(tmpdir);
|
||||||
|
strBuilder.append(",");
|
||||||
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
String bootJar = artemisHome + File.separator + "lib" + File.separator + "artemis-boot.jar";
|
||||||
|
|
||||||
|
String[] command = {"java", "-cp", bootJar, "org.apache.activemq.artemis.boot.WebTmpCleaner", strBuilder.toString()};
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(command);
|
||||||
|
|
||||||
|
pb.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
webContexts.clear();
|
webContexts.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue