Skip to content

Commit 42ffbe9

Browse files
committed
Fix temporary workspace deletion on JVM shutdown (#967)
1 parent 7f2d9e7 commit 42ffbe9

File tree

5 files changed

+63
-19
lines changed

5 files changed

+63
-19
lines changed

_ext/eclipse-base/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.2.1`).
44

55
## [Unreleased]
6+
### Fixed
7+
* Racing condition when cleaning up temporary workspace on JVM runtime shutdown (see [#967](https://github.com/diffplug/spotless/issues/967)). Can lead to error logs and remaining files in workspace.
68

79
## [3.5.0] - 2021-06-20
810
### Added

_ext/eclipse-base/src/main/java/com/diffplug/spotless/extra/eclipse/base/SpotlessEclipseFramework.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,14 @@ public void activatePlugins(SpotlessEclipsePluginConfig config) {
222222
public synchronized static void setup(SpotlessEclipseConfig config) throws BundleException {
223223
if (null == INSTANCE) {
224224
INSTANCE = new SpotlessEclipseFramework();
225+
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
226+
try {
227+
INSTANCE.shutdown();
228+
} catch (Exception e) {
229+
//At shutdown everything is just done on best-efforts basis
230+
}
231+
}));
232+
225233
config.registerServices(INSTANCE.getServiceConfig());
226234

227235
SpotlessEclipseCoreConfig coreConfig = new SpotlessEclipseCoreConfig();
@@ -252,6 +260,10 @@ private SpotlessEclipseFramework() throws BundleException {
252260
};
253261
}
254262

263+
void shutdown() {
264+
controller.shutdown();
265+
}
266+
255267
private SpotlessEclipseServiceConfig getServiceConfig() {
256268
return controller.getServices();
257269
}

_ext/eclipse-base/src/main/java/com/diffplug/spotless/extra/eclipse/base/osgi/BundleController.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016 DiffPlug
2+
* Copyright 2016-2021 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -80,6 +80,17 @@ public BundleController() throws BundleException {
8080
FrameworkBundleRegistry.initialize(this);
8181
}
8282

83+
public void shutdown() {
84+
bundles.getAll().forEach(b -> {
85+
try {
86+
b.stop();
87+
} catch (IllegalStateException | BundleException e) {
88+
//Stop on best effort basis
89+
}
90+
});
91+
services.stop();
92+
}
93+
8394
public ServiceCollection getServices() {
8495
return services;
8596
}

_ext/eclipse-base/src/main/java/com/diffplug/spotless/extra/eclipse/base/osgi/ServiceCollection.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 DiffPlug
2+
* Copyright 2016-2021 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,10 +15,12 @@
1515
*/
1616
package com.diffplug.spotless.extra.eclipse.base.osgi;
1717

18+
import java.util.ArrayList;
1819
import java.util.Collection;
1920
import java.util.Dictionary;
2021
import java.util.HashMap;
2122
import java.util.Hashtable;
23+
import java.util.List;
2224
import java.util.Map;
2325
import java.util.Optional;
2426

@@ -33,11 +35,15 @@
3335

3436
/**
3537
* Collection of services.
38+
* Eclipse service are not expected to hold any resources. Spotless services
39+
* can implement AutoCloseable in case a resource release is required on shutdown.
40+
*
3641
* Note that the collection access is not thread save, since it is expected
3742
* that the collection is completed before starting any bundles.
3843
*/
3944
public class ServiceCollection implements SpotlessEclipseServiceConfig {
4045
private final Map<String, ServiceReference<?>> className2Service;
46+
private final List<AutoCloseable> servicesWithResources;
4147
private final Bundle systemBundle;
4248
private final Map<String, String> properties;
4349

@@ -48,10 +54,21 @@ public class ServiceCollection implements SpotlessEclipseServiceConfig {
4854
*/
4955
ServiceCollection(Bundle systemBundle, Map<String, String> properties) {
5056
className2Service = new HashMap<String, ServiceReference<?>>();
57+
servicesWithResources = new ArrayList<>();
5158
this.systemBundle = systemBundle;
5259
this.properties = properties;
5360
}
5461

62+
void stop() {
63+
servicesWithResources.stream().forEach(s -> {
64+
try {
65+
s.close();
66+
} catch (Exception e) {
67+
//Stop on best effort basis
68+
}
69+
});
70+
}
71+
5572
@Override
5673
public void set(String key, String value) {
5774
properties.put(key, value);
@@ -64,6 +81,9 @@ public <S> void add(Class<S> interfaceClass, S service) throws ServiceException
6481
throw new ServiceException(
6582
String.format("Service '%s' is already registered.", interfaceClass.getName()), ServiceException.FACTORY_ERROR);
6683
}
84+
if (service instanceof AutoCloseable) {
85+
servicesWithResources.add((AutoCloseable) service);
86+
}
6787
}
6888

6989
/** Creates filter object suitable to lookup service by its interface name. */

_ext/eclipse-base/src/main/java/com/diffplug/spotless/extra/eclipse/base/service/TemporaryLocation.java

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016 DiffPlug
2+
* Copyright 2016-2021 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
2828
import org.eclipse.osgi.service.datalocation.Location;
2929

3030
/** All files generated at runtime are stored in a temporary location. */
31-
public class TemporaryLocation implements Location {
31+
public class TemporaryLocation implements Location, AutoCloseable {
3232
private static final String TEMP_PREFIX = "com_diffplug_spotless_extra_eclipse";
3333
private final URL location;
3434
private Location parent;
@@ -45,27 +45,12 @@ private TemporaryLocation(Location parent, URL defaultValue) {
4545
private static URL createTemporaryDirectory() {
4646
try {
4747
Path location = Files.createTempDirectory(TEMP_PREFIX);
48-
deleteDirectoryRecursivelyOnExit(location);
49-
location.toFile().deleteOnExit();
5048
return location.toUri().toURL();
5149
} catch (IOException e) {
5250
throw new IOError(e);
5351
}
5452
}
5553

56-
private static void deleteDirectoryRecursivelyOnExit(Path location) {
57-
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
58-
try {
59-
Files.walk(location)
60-
.sorted(Comparator.reverseOrder())
61-
.map(Path::toFile)
62-
.forEach(File::delete);
63-
} catch (IOException e) {
64-
//At shutdown everything is just done on best-efforts basis
65-
}
66-
}));
67-
}
68-
6954
@Override
7055
public boolean allowsDefault() {
7156
return false;
@@ -142,4 +127,18 @@ public URL getDataArea(String path) throws IOException {
142127
}
143128
}
144129

130+
@Override
131+
public void close() throws Exception {
132+
try {
133+
Path path = Path.of(location.toURI());
134+
Files.walk(path)
135+
.sorted(Comparator.reverseOrder())
136+
.map(Path::toFile)
137+
.forEach(File::delete);
138+
path.toFile().delete();
139+
} catch (IOException e) {
140+
//At shutdown everything is just done on best-efforts basis
141+
}
142+
}
143+
145144
}

0 commit comments

Comments
 (0)