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  }