001    /*
002     * Copyright (c) 2007-2014 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.PriorityQueue;
025    
026    import org.slf4j.Logger;
027    import org.slf4j.LoggerFactory;
028    
029    /**
030     * ShutdownUtil is a private helper class for registering dependent shutdown hooks to maintain internal state
031     * information reliably when a jvm is shutting down.
032     * <p/>
033     * This api is not intended for public use.
034     */
035    public class ShutdownUtil
036      {
037      private static final Logger LOG = LoggerFactory.getLogger( ShutdownUtil.class );
038    
039      public static abstract class Hook
040        {
041        public enum Priority
042          {
043            FIRST, WORK_PARENT, WORK_CHILD, SERVICE_CONSUMER, SERVICE_PROVIDER, LAST
044          }
045    
046        public abstract Priority priority();
047    
048        public abstract void execute();
049        }
050    
051      private static PriorityQueue<Hook> queue = new PriorityQueue<Hook>( 20, new Comparator<Hook>()
052      {
053      @Override
054      public int compare( Hook lhs, Hook rhs )
055        {
056        if( lhs == rhs )
057          return 0;
058    
059        if( lhs == null )
060          return -1;
061        else if( rhs == null )
062          return 1;
063    
064        return lhs.priority().compareTo( rhs.priority() );
065        }
066      }
067      );
068    
069      private static Thread shutdownHook;
070    
071      public static void addHook( Hook hook )
072        {
073        if( hook == null )
074          throw new IllegalArgumentException( "hook may not be null" );
075    
076        registerShutdownHook();
077    
078        queue.add( hook );
079        }
080    
081      public static boolean removeHook( Hook hook )
082        {
083        return queue.remove( hook );
084        }
085    
086      public static synchronized void registerShutdownHook()
087        {
088        if( shutdownHook != null )
089          return;
090    
091        shutdownHook = new Thread( "cascading shutdown hooks" )
092        {
093        @Override
094        public void run()
095          {
096          // These are not threads, so each one will be run in priority order
097          // blocking until the previous is complete
098          while( !queue.isEmpty() )
099            {
100            Hook hook = null;
101    
102            try
103              {
104              hook = queue.poll();
105    
106              hook.execute();
107              }
108            catch( Exception exception )
109              {
110              LOG.error( "failed executing hook: {}, with exception: {}", hook, exception );
111              }
112            }
113          }
114        };
115    
116        Runtime.getRuntime().addShutdownHook( shutdownHook );
117        }
118    
119      public static void deregisterShutdownHook()
120        {
121        Runtime.getRuntime().removeShutdownHook( shutdownHook );
122        }
123      }