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