Skip to content

Commit 6aff480

Browse files
committed
Add shutdown hook
Add shutdown hook to Runtime which will call EngineWrapper.close. This should help ensure Engine.close is called (and therefore license is released) in some situations where finally block may be subverted, for example if System.exit is called within user's Consumer<Utilities> accept method.
1 parent 9e10c19 commit 6aff480

1 file changed

Lines changed: 42 additions & 13 deletions

File tree

Java/src/main/java/com/nuix/javaenginesimple/EngineWrapper.java

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* @author Jason Wells
2727
*
2828
*/
29-
public class EngineWrapper {
29+
public class EngineWrapper implements AutoCloseable {
3030
// Obtain a logger instance for this class
3131
private final static Logger logger = Logger.getLogger("EngineWrapper");
3232

@@ -35,6 +35,8 @@ public class EngineWrapper {
3535
private File nuixBaseDirectory = null;
3636
private Engine engine = null;
3737

38+
private Thread shutdownHook = null;
39+
3840
// We will use this while iterating licenses to determine which one to acquire.
3941
// By default this filter accepts any license.
4042
private LicenseFilter licenseFilter = new LicenseFilter();
@@ -55,6 +57,15 @@ public EngineWrapper(File nuixBaseDirectory){
5557
File engineLibDirectory = new File(nuixBaseDirectory,"lib");
5658
logger.info(String.format("Setting 'nuix.libdir' to: %s", engineLibDirectory.getAbsolutePath()));
5759
System.setProperty("nuix.libdir", engineLibDirectory.getAbsolutePath());
60+
61+
// Whenever we create an instance of the engine to hand over to the user, we will register
62+
// our shutdown hook to close this instance. This helps to ensure that the license is released
63+
// in some scenarios where the finally block may not execute, such as code calling System.exit before
64+
// returning from user provided Consumer<Utilities>.
65+
shutdownHook = new Thread(() -> {
66+
try { close(); } catch (Exception e)
67+
{ logger.error("Error in shutdown hook",e); }
68+
});
5869
}
5970

6071
/***
@@ -101,6 +112,11 @@ public void withDongleLicense(Consumer<Utilities> consumer) throws Exception{
101112
if(licenseObtained){
102113
Utilities utilities = engine.getUtilities();
103114
ThirdPartyDependencyChecker.logAllDependencyInfo(utilities);
115+
116+
// Setup our shutdown hook in case user terminates before consumer returns
117+
logger.info("Adding shutdown hook for EngineWrapper::close");
118+
Runtime.getRuntime().addShutdownHook(shutdownHook);
119+
104120
logger.info("License was obtained, providing Utilities object to consumer...");
105121
consumer.accept(utilities);
106122
}
@@ -112,10 +128,7 @@ public void withDongleLicense(Consumer<Utilities> consumer) throws Exception{
112128
logger.error("Error while creating Engine instance",engineException);
113129
throw engineException;
114130
} finally {
115-
if(engine != null){
116-
logger.info("Closing Engine instance...");
117-
engine.close();
118-
}
131+
close();
119132
}
120133
} catch (Exception globalContainerException) {
121134
logger.error("Error while creating GlobalContainer",globalContainerException);
@@ -209,6 +222,11 @@ public void execute(CredentialsCallbackInfo info) {
209222
if(licenseObtained){
210223
Utilities utilities = engine.getUtilities();
211224
ThirdPartyDependencyChecker.logAllDependencyInfo(utilities);
225+
226+
// Setup our shutdown hook in case user terminates before consumer returns
227+
logger.info("Adding shutdown hook for EngineWrapper::close");
228+
Runtime.getRuntime().addShutdownHook(shutdownHook);
229+
212230
logger.info("License was obtained, providing Utilities object to consumer...");
213231
consumer.accept(utilities);
214232
} else {
@@ -222,10 +240,7 @@ public void execute(CredentialsCallbackInfo info) {
222240
logger.error("Error while creating Engine instance",engineException);
223241
throw engineException;
224242
} finally {
225-
if(engine != null){
226-
logger.info("Closing Engine instance...");
227-
engine.close();
228-
}
243+
close();
229244
}
230245
} catch (Exception globalContainerException) {
231246
logger.error("Error while creating GlobalContainer",globalContainerException);
@@ -302,6 +317,11 @@ public void execute(CredentialsCallbackInfo info) {
302317
if(licenseObtained){
303318
Utilities utilities = engine.getUtilities();
304319
ThirdPartyDependencyChecker.logAllDependencyInfo(utilities);
320+
321+
// Setup our shutdown hook in case user terminates before consumer returns
322+
logger.info("Adding shutdown hook for EngineWrapper::close");
323+
Runtime.getRuntime().addShutdownHook(shutdownHook);
324+
305325
logger.info("License was obtained, providing Utilities object to consumer...");
306326
consumer.accept(utilities);
307327
} else {
@@ -315,10 +335,7 @@ public void execute(CredentialsCallbackInfo info) {
315335
logger.error("Error while creating Engine instance",engineException);
316336
throw engineException;
317337
} finally {
318-
if(engine != null){
319-
logger.info("Closing Engine instance...");
320-
engine.close();
321-
}
338+
close();
322339
}
323340
} catch (Exception globalContainerException) {
324341
logger.error("Error while creating GlobalContainer",globalContainerException);
@@ -477,4 +494,16 @@ public LicenseFilter getLicenseFilter() {
477494
public void setLicenseFilter(LicenseFilter licenseFilter) {
478495
this.licenseFilter = licenseFilter;
479496
}
497+
498+
@Override
499+
public void close() throws Exception {
500+
if(engine != null) {
501+
logger.info("Closing engine instance");
502+
engine.close();
503+
}
504+
505+
// Remove our shutdown hook since engine has now been closed
506+
logger.info("Removing shutdown hook to EngineWrapper::close");
507+
Runtime.getRuntime().removeShutdownHook(shutdownHook);
508+
}
480509
}

0 commit comments

Comments
 (0)