/*
* Copyright (C) 2011 Laurent Caillette
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.outfit.shell.insider;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
/**
* A Java agent that works in 2 ways:
* <ul>
* <li>As pre-main at JVM startup for installing {@link Insider JMX bean}.
* <li>After JVM startup for executing a {@link Runtime#halt(int)} immediately.
* </ul>
* <p>
* The JMX-based approach respects the Java Language Specification as it doesn't rely on
* proprietary API. It is also faster.
* <p>
* The hot load of a Java agent relies on one of Sun's proprietary API. It makes sense
* after a (lengthy) scan for JVMs on the local machine which have the
* "tattoo" (see {@code org.novelang.outfit.shell.ShutdownTools.SHUTDOWN_TATTOO_PROPERTYNAME}).
* But it's a full Java, multiplatform mean to shutdown a JVM.
*
*
* @author Laurent Caillette
*/
@SuppressWarnings( { "UnusedDeclaration", "UseOfSystemOutOrSystemErr" } )
public class InsiderAgent {
private InsiderAgent() { }
/**
* Called when launching a JVM with {@code -javaagent} option.
* See <a href="http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-summary.html">Java Instrumentation specification</a>.
*/
public static void premain( final String arguments ) {
final MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer() ;
final LocalInsider managedBean ;
try {
if( arguments != null
&& arguments.startsWith( Insider.MAXIMUM_HEARTBEATDELAY_PARAMETERNAME )
) out : {
final String delayAsString = arguments.substring(
Insider.MAXIMUM_HEARTBEATDELAY_PARAMETERNAME.length() ) ;
final long delay;
try {
delay = Long.parseLong( delayAsString );
} catch( NumberFormatException e ) {
System.err.println( "Couldn't parse arguments '" + arguments + "', using defaults." ) ;
managedBean = new LocalInsider() ;
break out ;
}
managedBean = new LocalInsider( delay ) ;
} else {
managedBean = new LocalInsider() ;
}
beanServer.registerMBean( managedBean, Insider.NAME ) ;
} catch( Exception e ) {
// Checked exceptions in method signature prevent the JVM from loading the agent.
throw new RuntimeException( e ) ;
}
System.out.println( "Loaded " + InsiderAgent.class.getName()
+ " inside " + managedBean.getVirtualMachineName() + "." ) ;
}
/**
* Called when hot-loaded through
* <a href="http://java.sun.com/javase/6/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html#loadAgent%28java.lang.String,%20java.lang.String%29">Attach API</a>.
*/
public static void agentmain( final String args ) {
new Thread( new Runnable() {
@Override
public void run()
{
System.out.println( getClass().getName() + " halting JVM " + "..." ) ;
Runtime.getRuntime().halt( 1 ) ;
}
} ).start() ;
}
}