package de.skuzzle.polly.core;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.management.ManagementFactory;
import java.nio.channels.FileLock;
import java.util.Arrays;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import de.skuzzle.polly.core.commandline.AbstractArgumentParser;
import de.skuzzle.polly.core.commandline.ParameterException;
import de.skuzzle.polly.core.commandline.PollyArgumentParser;
import de.skuzzle.polly.core.configuration.ConfigurationProviderImpl;
import de.skuzzle.polly.core.internal.ModuleBootstrapper;
import de.skuzzle.polly.core.internal.ShutdownManagerImpl;
import de.skuzzle.polly.core.moduleloader.AbstractProvider;
import de.skuzzle.polly.core.moduleloader.DefaultModuleLoader;
import de.skuzzle.polly.core.moduleloader.ModuleLoader;
import de.skuzzle.polly.core.moduleloader.SetupException;
import de.skuzzle.polly.core.moduleloader.annotations.Module;
import de.skuzzle.polly.core.moduleloader.annotations.Provide;
import de.skuzzle.polly.core.util.ProxyClassLoader;
import de.skuzzle.polly.sdk.Configuration;
import de.skuzzle.polly.sdk.Version;
import de.skuzzle.polly.sdk.resources.LocaleProvider;
public class Polly {
public static Version getPollyVersion() {
final String version = Polly.class.getPackage().getImplementationVersion();
if (version == null) {
return new Version(DEVELOP_VERSION);
} else {
return new Version(version);
}
}
public static String getPid() {
final String[] parts = ManagementFactory.getRuntimeMXBean().getName()
.split("@"); //$NON-NLS-1$
return parts[0];
}
/**
* If polly transforms into SkyNet, the assumption is that this method returns true.
* In order to save the world, any polly start up is prevented in this case.
* @return Whether polly transformed into skynet.
*/
private static boolean isSkynet() {
return false;
}
public static File getPollyPath() {
return new File("."); //$NON-NLS-1$
}
private static String commandLine = ""; //$NON-NLS-1$
private static String[] commandLineArgs = {};
public static String getCommandLine() {
return commandLine;
}
public static String[] getCommandLineArgs() {
return commandLineArgs;
}
public final static String DEVELOP_VERSION = "0.9.1 - dev"; //$NON-NLS-1$
public final static String CONFIG_FULL_PATH = "cfg/polly.cfg"; //$NON-NLS-1$
public final static String CONFIG_FILE_NAME = "polly.cfg"; //$NON-NLS-1$
public final static String CONFIG_DIR = "./cfg"; //$NON-NLS-1$
/**
* The polly plugin folder. That folder is not supposed to change
*/
public final static String PLUGIN_FOLDER = "plugins"; //$NON-NLS-1$
private static Logger logger = Logger.getLogger(Polly.class.getName());
/**
* Start polly.
*
* @param args The commandline arguments passed to polly. This array may be empty.
* @throws Exception If launching fails.
*/
public synchronized static void main(String[] args) throws Exception {
// First thing we do is to check for skynet
if (isSkynet()) {
System.out.println("Polly transformed into SkyNet. Saving the world by immediate shut down");
return;
}
commandLineArgs = args;
for (final String arg : args) {
if (arg.indexOf(' ') != -1) {
commandLine += "\"" + arg + "\" "; //$NON-NLS-1$ //$NON-NLS-2$
} else {
commandLine += arg + " "; //$NON-NLS-1$
}
}
commandLine = commandLine.trim();
final ProxyClassLoader parentCl = (ProxyClassLoader)
Thread.currentThread().getContextClassLoader();
new Polly(args, parentCl);
}
public Polly(String[] args, ProxyClassLoader parentCl) {
final ConfigurationProviderImpl configurationProvider =
new ConfigurationProviderImpl(new File(CONFIG_DIR));
Configuration pollyCfg = null;
try {
System.out.println("Loading main configuration file"); //$NON-NLS-1$
pollyCfg = configurationProvider.open(CONFIG_FILE_NAME, true,
ConfigurationProviderImpl.NOP_VALIDATOR);
System.out.println("Configuring Logger..."); //$NON-NLS-1$
PropertyConfigurator.configure(
pollyCfg.readString(Configuration.LOG_CONFIG_FILE));
System.out.println("Logger configured. Switching to using logger"); //$NON-NLS-1$
// initialize Locale as early as possible
LocaleProvider.initLocale(pollyCfg);
if (!lockInstance(new File("polly.lck"))) { //$NON-NLS-1$
System.out.println(MSG.alreadyRunning);
return;
}
} catch (final FileNotFoundException e) {
System.out.println(MSG.bind(MSG.configError,
MSG.bind(MSG.configErrorReason1, CONFIG_FILE_NAME)));
e.printStackTrace();
} catch (final IOException e) {
System.out.println(MSG.bind(MSG.configError,
MSG.bind(MSG.configErrorReason2, e.getMessage())));
e.printStackTrace();
} catch (final ConfigurationException e) {
System.out.println(MSG.bind(MSG.configError,
MSG.bind(MSG.configErrorReason3, e.getMessage())));
e.printStackTrace();
}
if (pollyCfg == null) {
return;
}
logger.info(""); //$NON-NLS-1$
logger.info(""); //$NON-NLS-1$
logger.info(""); //$NON-NLS-1$
parseArguments(args, pollyCfg);
logger.info("----------------------------------------------"); //$NON-NLS-1$
logger.info("new polly session started!"); //$NON-NLS-1$
logger.info("----------------------------------------------"); //$NON-NLS-1$
logger.info(""); //$NON-NLS-1$
logger.info("Config file read from '" + CONFIG_FULL_PATH + "'"); //$NON-NLS-1$ //$NON-NLS-2$
logger.info("Polly command line arguments: " + Arrays.toString(args)); //$NON-NLS-1$
logger.info("(Canonical: " + getCommandLine() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
final Version version = getPollyVersion();
logger.info("Version info: " + version); //$NON-NLS-1$
logger.info("Java Version: " + System.getProperty("java.version")); //$NON-NLS-1$ //$NON-NLS-2$
logger.info("Java Home: " + System.getProperty("java.home")); //$NON-NLS-1$ //$NON-NLS-2$
logger.info("Classpath: \n " + System.getProperty("java.class.path").replace( //$NON-NLS-1$ //$NON-NLS-2$
File.pathSeparator, "\n ")); //$NON-NLS-1$
/*
* POLLY INITIALIZATION:
*
* Each component to be set up has its own Module subclass which does
* all the set up things for this component. While setting up, this
* module can provide all components that it has initialized to the
* other modules that will be setup next. This process is executed by
* the ModuleBlackboard.
*
* Each module must report its dependencies and provided components in the
* constructor. Given that information for every single module, the module
* loader determines the order in which the modules are loaded so all
* dependencies can be satisfied.
*
* The modules to be loaded must be specified in the modules.cfg as pointed to
* by Configuration#MODULES_CONFIG.
*/
// Setup Module loader
final ModuleLoader loader = new DefaultModuleLoader();
// Modules:
// Register all the modules. The order in which they are registered does not
// matter as the setup order will be automatically determined by the
// ModuleLoader. Just make sure your module properly reports all its
// dependencies!
try {
// This parses the modules.cfg and instantiates all listed modules
final File modulesCfg = new File(pollyCfg.readString(Configuration.MODULE_CONFIG));
ModuleBootstrapper.prepareModuleLoader(loader, modulesCfg);
// add startup module
new AnonymousModule(loader, configurationProvider, parentCl);
logger.info("Running setup for all modules..."); //$NON-NLS-1$
loader.runSetup();
logger.info("Setup done. Now executing each module..."); //$NON-NLS-1$
loader.runModules();
if (pollyCfg.readBoolean(Configuration.DEBUG_MODE)) {
loader.exportToDot(new File("modules.dot")); //$NON-NLS-1$
}
} catch (final Exception e) {
logger.fatal("Crucial component failed to load!", e); //$NON-NLS-1$
final ShutdownManagerImpl s = loader.requireNow(ShutdownManagerImpl.class);
s.shutdown(true);
} finally {
loader.dispose();
}
logger.info("Polly succesfully set up."); //$NON-NLS-1$
}
@Module(startUp = true, provides = {
@Provide(component = ShutdownManagerImpl.class),
@Provide(component = ConfigurationProviderImpl.class),
@Provide(component = ProxyClassLoader.class),
})
private static class AnonymousModule extends AbstractProvider {
private final ConfigurationProviderImpl configurationProvider;
private final ProxyClassLoader parentCl;
public AnonymousModule(ModuleLoader loader,
ConfigurationProviderImpl configurationProvider,
ProxyClassLoader parentCl) {
super("ROOT_PROVIDER", loader, true); //$NON-NLS-1$
this.parentCl = parentCl;
this.configurationProvider = configurationProvider;
}
@Override
public void setup() throws SetupException {
provideComponent(this.configurationProvider);
provideComponent(this.parentCl);
provideComponent(new ShutdownManagerImpl());
if (this.configurationProvider.getRootConfiguration().readBoolean(
Configuration.DEBUG_MODE)) {
this.logger.warn(
"\n\nPOLLY DEBUG MODE IS ENABLED. DISABLE THIS IN PRODUCTIVE SYSTEMS!\n\n"); //$NON-NLS-1$
}
}
}
private boolean lockInstance(final File file) {
try {
final boolean existed = file.exists();
if (!existed) {
file.createNewFile();
}
final RandomAccessFile randomAccessFile = new RandomAccessFile(
file, "rw"); //$NON-NLS-1$
final FileLock fileLock = randomAccessFile.getChannel().tryLock();
if (fileLock != null) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
fileLock.release();
randomAccessFile.close();
if (!existed) {
file.delete();
}
} catch (final Exception e) {
e.printStackTrace();
}
}
});
return true;
}
} catch (final Exception e) {
e.printStackTrace();
}
return false;
}
private void parseArguments(String[] args, Configuration config) {
try {
final AbstractArgumentParser parser = new PollyArgumentParser(config);
parser.parse(args);
commandLine = parser.getCanonicalArguments();
} catch (final ParameterException e) {
logger.warn(e.getMessage(), e);
printHelp();
System.exit(0);
}
}
private void printHelp() {
System.out.println(MSG.paramCaption);
System.out.println(MSG.paramHeader);
System.out.println(MSG.paramLog);
System.out.println(MSG.paramNick);
System.out.println(MSG.paramIdent);
System.out.println(MSG.paramServer);
System.out.println(MSG.paramPort);
System.out.println(MSG.paramJoin);
System.out.println(MSG.paramHelp);
System.out.println(MSG.paramClose);
try {
System.in.read();
} catch (final IOException e) {
e.printStackTrace();
}
}
}