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
021package cascading.util;
022
023import java.util.Comparator;
024import java.util.Queue;
025import java.util.concurrent.PriorityBlockingQueue;
026
027import org.slf4j.Logger;
028import 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 */
036public class ShutdownUtil
037  {
038  /**
039   * System property set to true when we have entered shutdown
040   */
041  public static final String SHUTDOWN_EXECUTING = "cascading.jvm.shutdown.executing";
042
043  private static final Logger LOG = LoggerFactory.getLogger( ShutdownUtil.class );
044
045  public static abstract class Hook
046    {
047    public enum Priority
048      {
049        FIRST, WORK_PARENT, WORK_CHILD, SERVICE_CONSUMER, SERVICE_PROVIDER, LAST
050      }
051
052    public abstract Priority priority();
053
054    public abstract void execute();
055    }
056
057  private static Queue<Hook> queue = new PriorityBlockingQueue<>( 20, new Comparator<Hook>()
058  {
059  @Override
060  public int compare( Hook lhs, Hook rhs )
061    {
062    if( lhs == rhs )
063      return 0;
064
065    if( lhs == null )
066      return -1;
067    else if( rhs == null )
068      return 1;
069
070    return lhs.priority().compareTo( rhs.priority() );
071    }
072  }
073  );
074
075  private static Thread shutdownHook;
076
077  public static void addHook( Hook hook )
078    {
079    if( hook == null )
080      throw new IllegalArgumentException( "hook may not be null" );
081
082    registerShutdownHook();
083
084    queue.add( hook );
085    }
086
087  public static boolean removeHook( Hook hook )
088    {
089    return queue.remove( hook );
090    }
091
092  public static synchronized void registerShutdownHook()
093    {
094    if( shutdownHook != null )
095      return;
096
097    shutdownHook = new Thread( "cascading shutdown hooks" )
098    {
099    @Override
100    public void run()
101      {
102      System.setProperty( SHUTDOWN_EXECUTING, "true" );
103
104      try
105        {
106        // These are not threads, so each one will be run in priority order
107        // blocking until the previous is complete
108        while( !queue.isEmpty() )
109          {
110          Hook hook = null;
111
112          try
113            {
114            hook = queue.poll();
115
116            // may get removed while shutdown is executing
117            if( hook == null )
118              continue;
119
120            hook.execute();
121            }
122          catch( Exception exception )
123            {
124            LOG.error( "failed executing hook: {}, with exception: {}", hook, exception );
125            }
126          }
127        }
128      finally
129        {
130        System.setProperty( SHUTDOWN_EXECUTING, "false" );
131        }
132      }
133    };
134
135    Runtime.getRuntime().addShutdownHook( shutdownHook );
136    }
137
138  public static void deregisterShutdownHook()
139    {
140    Runtime.getRuntime().removeShutdownHook( shutdownHook );
141    }
142  }