
uk.org.retep.kernel.bootstrap.Bootstrap Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2009, Peter T Mount
* All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 .
*
*
*
* GNU GENERAL PUBLIC LICENSE - CLASSPATH EXCEPTION
*
*
*
* Linking this library statically or dynamically with other modules
* is making a combined work based on this library. Thus, the terms
* and conditions of the GNU General Public License cover the whole
* combination.
*
*
*
* As a special exception, the copyright holders of this library give
* you permission to link this library with independent modules to
* produce an executable, regardless of the license terms of these
* independent modules, and to copy and distribute the resulting
* executable under terms of your choice, provided that you also meet,
* for each linked independent module, the terms and conditions of the
* license of that module.
*
*
*
* An independent module is a module which is either not derived from or based
* on this library, or a module who's classes extend those within this library
* as part of the implementation of the library.
*
*
*
* If you modify this library, you may extend this exception to your version
* of the library, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*
*/
package uk.org.retep.kernel.bootstrap;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import uk.org.retep.kernel.module.Module;
import uk.org.retep.kernel.module.ModuleEvent;
/**
* The bootstrap class run by the native launchers.
*
*
* It runs in the system class loader so cannot have any dependencies
* outside of it's own jar other than the standard classes.
*
*
*
* It manages the configuration of the system classloaders, terracotta integration
* and the initialisation of the Kernel itself.
*
*
* @author peter
*/
public class Bootstrap
{
private static final Bootstrap instance = new Bootstrap();
private ClassLoader classLoader;
private String applicationName;
private List arguments = new ArrayList();
private Thread bootStrapThread;
private File homeDirectory;
private Date applicationStartDate;
private Set modules;
private BlockingDeque> eventQueue;
public static Bootstrap getInstance()
{
return instance;
}
private Bootstrap()
{
}
/**
* Used by JNI to set the application name
* @param applicationName
*/
public void setApplicationName( final String applicationName )
{
this.applicationName = applicationName;
// TODO check to see if this is true as the native launcher now does this...
// On Linux and Darwin, applicationName is just that, but on Win32
// when run from inside Cygwin it's the full path to the .exe so
// we have to do an inline StringUtils.basename() in that case
if( File.separatorChar == '\\' )
{
final int i = applicationName.lastIndexOf( '\\' );
if( i > -1 )
{
this.applicationName = applicationName.substring( i + 1 );
}
}
}
/**
* Used by JNI to add a command line argument
* @param arg command line argument
*/
public void addArgument( final String arg )
{
arguments.add( arg );
}
/**
* Used by JNI to determine if we are restarting the application.
* @return true if we are restarting
*/
public boolean isRestart()
{
return false;
}
/**
* Used by JNI - starts up the actual kernel
*/
public void bootstrap()
{
try
{
final long startTime = System.currentTimeMillis();
applicationStartDate = new Date( startTime );
if( bootStrapThread == null )
{
bootStrapThread = Thread.currentThread();
}
else
{
throw new SecurityException(
"Only the BootStrap Thread may call this method" );
}
eventQueue = new LinkedBlockingDeque>();
createClassLoader();
createModules();
startModules();
runEventQueue();
}
catch( Throwable t )
{
t.printStackTrace();
System.exit( 1 );
}
}
private void runEventQueue()
throws InterruptedException
{
while( true )
{
final ModuleEvent> event = eventQueue.poll( 1L, TimeUnit.SECONDS );
if( event != null )
{
relayEvent( event );
}
}
}
private void relayEvent( final ModuleEvent> event )
{
for( Module module : modules )
{
try
{
module.handleModuleEvent( event );
}
catch( Throwable t )
{
t.printStackTrace();
}
}
}
private URL getJarURL( final Class> clazz )
throws MalformedURLException
{
URL classURL = clazz.getResource( clazz.getSimpleName() + ".class" );
if( classURL == null )
{
return null;
}
String path = classURL.toString();
int i = path.indexOf( '!' );
if( i > -1 )
{
path = path.substring( 0, i );
}
if( path.startsWith( "jar:" ) )
{
path = path.substring( 4 );
}
return new URL( path );
}
private File getJarFile( final Class> clazz )
{
try
{
URL jarURL = getJarURL( clazz );
if( jarURL != null )
{
return new File( jarURL.getPath() ).getAbsoluteFile();
}
return null;
}
catch( MalformedURLException ex )
{
ex.printStackTrace();
return null;
}
}
private void createClassLoader()
throws MalformedURLException,
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException
{
// The jar is under bin (i.e. home/bin/app.jar) so this returns
// a File representing bin.
final File binDir = getJarFile( getClass() ).getParentFile();
// Now resolve home. If binDir is called MacOS then home is Resources
// otherwise its ..
// FIXME this is going to be in the binary
homeDirectory = binDir.getParentFile();
if( "MacOS".equals( binDir.getName() ) )
{
// For OSX home is the Resources directory
homeDirectory = new File( homeDirectory, "Resources" );
}
// Now according to the layout check commons then app specific jars
// and add to the List.
final File libDir = new File( homeDirectory, "lib" );
final String clusterName = System.getProperty( "retep.cluster.name",
applicationName );
classLoader = KernelClassLoader.create(
applicationName,
clusterName,
libDir,
Thread.currentThread().getContextClassLoader() );
Thread.currentThread().setContextClassLoader( classLoader );
}
private void createModules()
throws Exception
{
// Load the modules and add to the set. This defines the deployment order
modules = new HashSet();
final ServiceLoader sl = ServiceLoader.load( Module.class,
classLoader );
for( Module module : sl )
{
modules.add( module );
}
// FIXME the binary knows this already
final File configDirectory = new File( homeDirectory, "etc" );
// Point dataDirectory to data/appName or data/common
final File data1Dir = new File( homeDirectory, "data" );
File data2Dir = new File( data1Dir, applicationName );
File data3Dir = new File( data1Dir, "common" );
final File dataDirectory = data2Dir.exists() ? data2Dir : data3Dir;
// Now create them
for( Module module : modules )
{
module.setApplicationName( applicationName );
module.setApplicationStartDate( applicationStartDate );
module.setArguments( arguments );
module.setClassLoader( classLoader );
module.setConfigDirectory( configDirectory );
module.setDataDirectory( dataDirectory );
module.setEventDeque( eventQueue );
module.setHomeDirectory( homeDirectory );
}
}
private void startModules()
throws Throwable
{
for( Module module : modules )
{
module.start();
}
}
}