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