/*
* 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;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.Map;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Removes clutter from {@link JavaShell}.
* @author Laurent Caillette
*/
public class JavaShellTools {
public static final Runnable NULL_RUNNABLE = new Runnable() {
@Override
public void run() { }
} ;
private JavaShellTools() { }
/**
* Tries to obtain system process identifier of running JVM.
*
* @return {@value #UNKNOWN_PROCESS_ID} if not found, process identifier otherwise.
*
* @author from <a href="http://samuelsjoberg.com/archive/2006/12/jvm-pid" >Samuel Sjöberg</a>'s
* blog.
*/
public static int extractProcessId( final String jvmName ) {
final StringBuilder pid = new StringBuilder() ;
for( int i = 0, l = jvmName.length() ; i < l ; i++ ) {
if( Character.isDigit( jvmName.charAt( i ) ) ) {
pid.append( jvmName.charAt( i ) ) ;
} else if( pid.length() > 0 ) {
break ;
}
}
try {
return Integer.parseInt( pid.toString() ) ;
} catch( NumberFormatException ignored ) {
return UNKNOWN_PROCESS_ID;
}
}
public static final int UNDEFINED_PROCESS_ID = -1 ;
public static final int UNKNOWN_PROCESS_ID = -2 ;
public static final ObjectName RUNTIME_MX_BEAN_OBJECTNAME ;
static {
try {
RUNTIME_MX_BEAN_OBJECTNAME = new ObjectName( ManagementFactory.RUNTIME_MXBEAN_NAME ) ;
} catch( MalformedObjectNameException e ) {
throw new RuntimeException( e ) ;
}
}
// ====================================
// Java Util Logging configuration file
// ====================================
static final ImmutableList< String > JAVA_UTIL_LOGGING_CONFIGURATION = ImmutableList.of(
"handlers= java.util.logging.ConsoleHandler",
".level=INFO",
"",
"java.util.logging.FileHandler.pattern = %h/java%u.log",
"java.util.logging.FileHandler.limit = 50000",
"java.util.logging.FileHandler.count = 1",
"java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter",
"",
"java.util.logging.ConsoleHandler.level = FINEST",
"java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter",
"",
"javax.management.level=FINEST",
"javax.management.remote.level=FINER"
) ;
static final File JAVA_UTIL_LOGGING_CONFIGURATION_FILE ;
static {
try {
JAVA_UTIL_LOGGING_CONFIGURATION_FILE =
File.createTempFile( "javautillogging", "properties" ).getCanonicalFile() ;
FileUtils.writeLines( JavaShellTools.JAVA_UTIL_LOGGING_CONFIGURATION_FILE, JavaShellTools.JAVA_UTIL_LOGGING_CONFIGURATION ) ;
} catch( IOException e ) {
throw new RuntimeException( e ) ;
}
}
static ImmutableList< String > createProcessArguments(
final ImmutableList< String > jvmArguments,
final BootstrappingJmxKit jmxKit,
final Integer jmxPort,
final JavaClasses javaClasses,
final ImmutableList< String > programArguments,
final Integer heartbeatMaximumPeriod
) {
final List< String > argumentList = Lists.newArrayList() ;
// This is a very optimistic approach for obtaining Java executable.
// TODO: see how Ant's Java task solves this.
argumentList.add( "java" ) ;
argumentList.add( "-D" + ShutdownTools.SHUTDOWN_TATTOO_PROPERTYNAME ) ;
if( jmxKit == null ) {
Preconditions.checkArgument( jmxPort == null, "Null jmxKit implies null jmxPort" ) ;
}
else
{
checkNotNull( jmxPort ) ;
argumentList.addAll( jmxKit.getJvmProperties( jmxPort, heartbeatMaximumPeriod ) ) ;
}
// Log JMX activity. Didn't prove useful.
// argumentList.add( "-Djava.util.logging.config.file=" +
// JAVA_UTIL_LOGGING_CONFIGURATION_FILE.getAbsolutePath() ) ;
if( jvmArguments != null ) {
argumentList.addAll( jvmArguments ) ;
}
argumentList.addAll( javaClasses.asStringList() ) ;
argumentList.addAll( programArguments ) ;
return ImmutableList.copyOf( argumentList ) ;
}
// ===
// JMX
// ===
/**
* Unregisters JMX Beans and closes the {@link javax.management.remote.JMXConnector}s.
* This method should close the default JMX connector o {@link JavaShell} because, when there is one, there is always
* a registered {@link org.novelang.outfit.shell.insider.Insider} at startup so it should appear inside the
* {@code Map}.
*
*/
/*package*/ static void disconnectAll( final Map<JmxBeanKey, JmxBeanValue > connectedBeans ) {
// First, unregister all beans.
for( final JmxBeanValue value : connectedBeans.values() ) {
try {
value.getConnectionBundle().connection.unregisterMBean( value.getObjectName() ) ;
} catch( InstanceNotFoundException e ) {
logCouldntUnregister( value.getObjectName(), e ) ;
} catch( MBeanRegistrationException e ) {
logCouldntUnregister( value.getObjectName(), e ) ;
} catch( IOException e ) {
logCouldntUnregister( value.getObjectName(), e ) ;
}
}
// Now we can close safely. The JMXConnector#close() method has no effect when called more than once.
for( final JmxBeanValue value : connectedBeans.values() ) {
try {
value.getConnectionBundle().connector.close() ;
} catch( IOException e ) {
logCouldntUnregister( value.getConnectionBundle().connector, e ) ;
}
}
}
private static void logCouldntUnregister( final Object culprit, final Exception e ) {
/*
LOG.debug( "Couldn't disconnect or unregister " + culprit + ", cause: " + e.getClass() +
" (may be normal if other VM terminated)." ) ;
*/
}
}