001/* 002 * Copyright (c) 2016-2017 Chris K Wensel <chris@wensel.net>. All Rights Reserved. 003 * Copyright (c) 2007-2017 Xplenty, Inc. All Rights Reserved. 004 * 005 * Project and contact information: http://www.cascading.org/ 006 * 007 * This file is part of the Cascading project. 008 * 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 */ 021 022package cascading.util; 023 024import java.util.Comparator; 025import java.util.Queue; 026import java.util.concurrent.PriorityBlockingQueue; 027 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * ShutdownUtil is a private helper class for registering dependent shutdown hooks to maintain internal state 033 * information reliably when a jvm is shutting down. 034 * <p> 035 * This api is not intended for public use. 036 */ 037public class ShutdownUtil 038 { 039 /** 040 * System property set to true when we have entered shutdown 041 */ 042 public static final String SHUTDOWN_EXECUTING = "cascading.jvm.shutdown.executing"; 043 public static final String SHUTDOWN_FORCE_NON_DAEMON = "cascading.jvm.shutdown.non-daemon"; 044 045 private static final Logger LOG = LoggerFactory.getLogger( ShutdownUtil.class ); 046 047 public static abstract class Hook 048 { 049 public enum Priority 050 { 051 FIRST, WORK_PARENT, WORK_CHILD, SERVICE_CONSUMER, SERVICE_PROVIDER, LAST 052 } 053 054 public abstract Priority priority(); 055 056 public abstract void execute(); 057 } 058 059 private static Queue<Hook> queue = new PriorityBlockingQueue<>( 20, new Comparator<Hook>() 060 { 061 @Override 062 public int compare( Hook lhs, Hook rhs ) 063 { 064 if( lhs == rhs ) 065 return 0; 066 067 if( lhs == null ) 068 return -1; 069 else if( rhs == null ) 070 return 1; 071 072 return lhs.priority().compareTo( rhs.priority() ); 073 } 074 } 075 ); 076 077 private static Thread shutdownHook; 078 079 public static void addHook( Hook hook ) 080 { 081 if( hook == null ) 082 throw new IllegalArgumentException( "hook may not be null" ); 083 084 registerShutdownHook(); 085 086 queue.add( hook ); 087 } 088 089 public static boolean removeHook( Hook hook ) 090 { 091 return queue.remove( hook ); 092 } 093 094 public static synchronized void registerShutdownHook() 095 { 096 if( shutdownHook != null ) 097 return; 098 099 final boolean isForceNonDaemon = Boolean.getBoolean( SHUTDOWN_FORCE_NON_DAEMON ); 100 101 shutdownHook = new Thread( "cascading shutdown hooks" ) 102 { 103 104 { 105 if( isForceNonDaemon ) 106 this.setDaemon( false ); 107 } 108 109 @Override 110 public void run() 111 { 112 System.setProperty( SHUTDOWN_EXECUTING, "true" ); 113 114 try 115 { 116 // These are not threads, so each one will be run in priority order 117 // blocking until the previous is complete 118 while( !queue.isEmpty() ) 119 { 120 Hook hook = null; 121 122 try 123 { 124 hook = queue.poll(); 125 126 // may get removed while shutdown is executing 127 if( hook == null ) 128 continue; 129 130 hook.execute(); 131 } 132 catch( Exception exception ) 133 { 134 LOG.error( "failed executing hook: {}, with exception: {}", hook, exception.getMessage() ); 135 LOG.debug( "with exception trace", exception ); 136 } 137 } 138 } 139 finally 140 { 141 System.setProperty( SHUTDOWN_EXECUTING, "false" ); 142 } 143 } 144 }; 145 146 Runtime.getRuntime().addShutdownHook( shutdownHook ); 147 } 148 149 public static void deregisterShutdownHook() 150 { 151 Runtime.getRuntime().removeShutdownHook( shutdownHook ); 152 } 153 }