uk.org.retep.util.signal.Signal Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2008, Peter T Mount
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the retep.org.uk nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package uk.org.retep.util.signal;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* SignalManager provides access to ANSI/ISO C signal support if available by
* both the JVM (Sun) and the underlying platorm.
*
* It does this by utilising the sun.misc.Signal
class so breaks
* the usual coding guidelines by accessing the sun.*
packages, but
* this class uses reflection so that if that class is not available (i.e. on a
* non-Sun based JVM) then the signal support is quietly disabled.
*
* @author peter
*/
public class Signal
{
private static final String UNSUPPORTED = "Signals not supported by this platform or JVM";
private static final String SIGNAL_CLASS = "sun.misc.Signal";
private static final String SIGNAL_HANDLER_CLASS = "sun.misc.SignalHandler";
private static Class signalClass;
private static Class signalHandlerClass;
private static Constructor signalConstructor;
private static Method signalHandle;
private static Method getName;
private static final Lock lock = new ReentrantLock();
private static final Map signalNames = new HashMap();
private static final Map> handlers = new HashMap>();
private final String name;
private final Object signal;
private Signal( final String name )
{
this.name = name;
if( signalClass == null )
{
init();
}
try
{
signal = signalConstructor.newInstance( name );
}
catch( InvocationTargetException ite )
{
final Throwable t = ite.getCause();
if( t instanceof IllegalArgumentException )
{
throw (IllegalArgumentException) t;
}
else
{
throw new IllegalArgumentException( UNSUPPORTED, ite );
}
}
catch( Exception ex )
{
throw new IllegalArgumentException( UNSUPPORTED, ex );
}
}
public static Signal getSignal( final String name )
{
lock.lock();
try
{
Signal signal = signalNames.get( name );
if( signal == null )
{
signal = new Signal( name );
signalNames.put( name, signal );
}
return signal;
}
finally
{
lock.unlock();
}
}
@SuppressWarnings( "unchecked" )
private static synchronized void init()
throws IllegalArgumentException
{
if( signalClass == null )
{
try
{
signalClass = Class.forName( SIGNAL_CLASS );
signalHandlerClass = Class.forName( SIGNAL_HANDLER_CLASS );
signalConstructor = signalClass.getConstructor( String.class );
signalHandle = signalClass.getMethod( "handle",
signalClass,
signalHandlerClass );
getName = signalClass.getMethod( "getName" );
}
catch( Exception ex )
{
throw new IllegalArgumentException( UNSUPPORTED );
}
}
}
/**
* The name of this Signal
* @return the name of this signal
*/
public String getName()
{
return name;
}
@Override
public int hashCode()
{
return name.hashCode();
}
@Override
public String toString()
{
return name;
}
@Override
public boolean equals( final Object other )
{
if( this == other )
{
return true;
}
if( other == null || !(other instanceof Signal) )
{
return false;
}
return name.equals( Signal.class.cast( other ).name );
}
/**
* Register a SignalHandler to a Signal
* @param signal
* @param handler
* @throws java.lang.IllegalArgumentException
*/
public static void registerSignalHandler( final Signal signal,
final SignalHandler handler )
throws IllegalArgumentException
{
lock.lock();
try
{
Set signals = handlers.get( signal );
if( signals == null )
{
registerWithJVM( signal );
// If the proxy was registered successfully then add it to our map
signals = new LinkedHashSet();
handlers.put( signal, signals );
}
// Finally add the handler to the Set
signals.add( handler );
}
catch( Exception ex )
{
throw new IllegalArgumentException( ex );
}
finally
{
lock.unlock();
}
}
/**
* Create a proxy for sun.misc.SignalHandler and register it with
* sun.misc.Signal.
*
* @param signal
* @throws java.lang.IllegalAccessException
* @throws java.lang.IllegalArgumentException
* @throws java.lang.reflect.InvocationTargetException
*/
private static void registerWithJVM( final Signal signal )
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
{
if( signalClass == null )
{
init();
}
Object proxyHandler = Proxy.newProxyInstance(
signalClass.getClassLoader(), new Class[]
{
signalHandlerClass
}, new InvocationHandler()
{
@Override
public Object invoke( final Object arg0,
final Method arg1,
final Object[] arg2 )
throws Throwable
{
final String name = (String) getName.invoke( arg2[0] );
invokeSignalHandlers( getSignal( name ) );
return null;
}
} );
// Now register our proxy
signalHandle.invoke( null, signal.signal, proxyHandler );
}
/**
* unregister a SignalHandler for a Signal
* @param signal Signal
* @param handler SignalHandler to remove
* @return true if removed
*/
public static boolean unregisterSignalHandler( final Signal signal,
final SignalHandler handler )
{
lock.lock();
try
{
final Set signals = handlers.get( signal );
if( signals != null )
{
return signals.remove( handler );
}
return false;
}
finally
{
lock.unlock();
}
}
private static void invokeSignalHandlers( final Signal signal )
{
SignalHandler signals[] = null;
lock.lock();
try
{
final Set set = handlers.get( signal );
if( set != null )
{
signals = set.toArray( new SignalHandler[ set.size() ] );
}
}
finally
{
lock.unlock();
}
for( SignalHandler handler : signals )
{
try
{
handler.handleSignal( signal );
}
catch( Throwable t )
{
// ignore Throwable to allow the next SignalHandler to be invoked
}
}
}
}