/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.jstorm.utils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingDeque;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.Signal;
import sun.misc.SignalHandler;
/***
* Catching signal in java is very dangerous,
* Please do as less as possible
*/
public class JStormSignalHandler implements SignalHandler {
private static final Logger LOG = LoggerFactory.getLogger(JStormSignalHandler.class);
// It must be single instance
protected static JStormSignalHandler instance = null;
protected Map<Integer, String> signalMap = new HashMap<>();
protected Map<Integer, Runnable> signalHandlers = new HashMap<>();
protected Map<Integer, SignalHandler> oldSignalHandlers = new HashMap<>();
protected Thread signalThread;
protected LinkedBlockingDeque<Signal> waitingSignals = new LinkedBlockingDeque<>();
protected boolean isRunning = true;
public static JStormSignalHandler getInstance() {
synchronized (JStormSignalHandler.class) {
if (instance == null) {
instance = new JStormSignalHandler();
}
}
return instance;
}
protected JStormSignalHandler() {
initSignalMap();
initSignalThread();
}
protected void initSignalMap() {
signalMap.put(1, "HUP");
signalMap.put(2, "INT");
signalMap.put(3, "QUIT");
signalMap.put(4, "ILL");
signalMap.put(5, "TRAP");
signalMap.put(6, "ABRT");
signalMap.put(6, "IOT");
signalMap.put(7, "BUS");
signalMap.put(8, "FPE");
signalMap.put(9, "KILL");
signalMap.put(10, "USR1");
signalMap.put(11, "SEGV");
signalMap.put(12, "USR2");
signalMap.put(13, "PIPE");
signalMap.put(14, "ALRM");
signalMap.put(15, "TERM");
signalMap.put(16, "STKFLT");
signalMap.put(17, "CHLD");
signalMap.put(18, "CONT");
signalMap.put(19, "STOP");
signalMap.put(20, "TSTP");
signalMap.put(21, "TTIN");
signalMap.put(22, "TTOU");
signalMap.put(23, "URG");
signalMap.put(24, "XCPU");
signalMap.put(25, "XFSZ");
signalMap.put(26, "VTALRM");
signalMap.put(27, "PROF");
signalMap.put(28, "WINCH");
signalMap.put(29, "IO");
signalMap.put(30, "PWR");
signalMap.put(31, "SYS");
signalMap.put(32, "UNUSED");
}
protected void initSignalThread() {
signalThread = new Thread(new SignalRunnable());
signalThread.setDaemon(true);
signalThread.setName("SignalRunnable");
signalThread.start();
}
/**
* Register signal to system
* if callback is null, then the current process will ignore this signal
*/
public synchronized void registerSignal(int signalNumber, Runnable callback, boolean replace) {
String signalName = signalMap.get(signalNumber);
if (signalName == null) {
LOG.warn("Invalid signalNumber " + signalNumber);
return;
}
LOG.info("Begin to register signal of {}", signalName);
try {
SignalHandler oldHandler = Signal.handle(new Signal(signalName), this);
LOG.info("Successfully register {} handler", signalName);
Runnable old = signalHandlers.put(signalNumber, callback);
if (old != null) {
if (!replace) {
oldSignalHandlers.put(signalNumber, oldHandler);
} else {
LOG.info("Successfully old {} handler will be replaced", signalName);
}
}
LOG.info("Successfully register signal of {}", signalName);
} catch (Exception e) {
LOG.error("Failed to register " + signalName + ":" + signalNumber + ", Signal already used by VM or OS: SIGILL");
}
}
protected class SignalRunnable implements Runnable {
@Override
public void run() {
LOG.info("Start");
while (isRunning) {
Signal signal;
try {
signal = waitingSignals.take();
if (signal != null) {
handle(signal);
}
} catch (Throwable e) {
LOG.error("Failed to handle " + e.getCause(), e);
}
}
LOG.info("End");
}
public void handle(Signal signal) {
LOG.info("Receive singal " + signal.getName() + " " + signal.getNumber());
Runnable runner = signalHandlers.get(signal.getNumber());
try {
if (runner == null) {
LOG.info("Skip JStorm register handler of signal: {}", signal.getName());
} else {
LOG.info("Begin to handle signal of {}", signal.getName());
runner.run();
LOG.info("Successfully handle signal of {}", signal.getName());
}
} catch (Throwable e) {
LOG.error("Failed to handle signal of " + signal.getName()
+ ":" + e.getCause(), e);
}
try {
SignalHandler oldHandler = oldSignalHandlers.get(signal.getNumber());
if (oldHandler != null) {
LOG.info("Begin to run the old singleHandler");
oldHandler.handle(signal);
// Force to sleep one second to avoid competition
JStormUtils.sleepMs(1000);
LOG.info("Successfully run the old singleHandler");
}
} catch (Throwable e) {
LOG.error("Failed to run old SignalHandler of signal: " + signal.getName()
+ ":" + e.getCause(), e);
}
}
}
public void handle(Signal signal) {
waitingSignals.offer(signal);
}
/**
* General "clean up" method which is called when we receive a TERM
* signal
* This will likely be superseeded by specific cleanup code
**/
public boolean cleanUp() {
LOG.info("Cleaning up!");
isRunning = false;
return true;
}
public static void registerJStormSignalHandler() {
JStormSignalHandler instance = JStormSignalHandler.getInstance();
int[] signals = {
1,
2,
3,
4,
5,
6,
7,
8,
10,
11,
12,
14,
16,
};
for (int signal : signals) {
instance.registerSignal(signal, null, false);
}
}
public static void main(final String[] args) {
JStormSignalHandler handler = JStormSignalHandler.getInstance();
registerJStormSignalHandler();
while (true) {
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}