package org.smartly.launcher;
import org.smartly.IConstants;
import org.smartly.Smartly;
import org.smartly.commons.cmdline.cmdparser.CmdLineParser;
import org.smartly.commons.lang.CharEncoding;
import org.smartly.commons.logging.LoggingRepository;
import org.smartly.commons.util.BeanUtils;
import org.smartly.commons.util.FileUtils;
import org.smartly.commons.util.PathUtils;
import org.smartly.commons.util.StringUtils;
import org.smartly.packages.SmartlyPackageLoader;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
abstract class AbstractLauncher {
// launcher args variables.
private static final String APPDIR = "APPDIR"; // user.dir
private static final String USERHOME = "USERHOME"; // user.home
private static final String DEFAULT_CLASSPATH = "lib/**";
private static final String SYSPROP_CLASSPATH = "smartly.classpath";
private static final String ENVPROP_HOME = "SMARTLY_HOME";
private static final String CLASSPATH_SMARTLY = Smartly.class.getCanonicalName();
private final String[] _args;
private final Map<String, Object> _argsMap;
private String[] _argsRemain;
private boolean _smartlyLauncher;
private Class _runnerClass;
private Object _runnerInstance;
private boolean _initialized;
protected AbstractLauncher(final String[] args) {
_args = args;
_argsMap = new HashMap<String, Object>();
_initialized = false;
try {
final ClassLoader loader = this.init(args);
_runnerClass = loader.loadClass(CLASSPATH_SMARTLY);
_runnerInstance = _runnerClass.newInstance();
// try inject args to runner
BeanUtils.setValueIfAny(_runnerInstance, "mappedArgs", _argsMap);
BeanUtils.setValueIfAny(_runnerInstance, "remainArgs", _argsRemain);
} catch (Exception x) {
System.err.println("Uncaught exception: ");
x.printStackTrace();
System.exit(2);
}
}
public final void run() {
if (!_initialized) {
runInternal();
}
_initialized = true;
}
public final String[] getArgs() {
return _args;
}
public final Map<String, Object> getArgsMap() {
return _argsMap;
}
public final boolean isSmartlyLauncher() {
return _smartlyLauncher;
}
/**
* Event emitter before Smartly started.
* Here you can register programmatically your package.
*
* @param loader Package Loader
*/
protected abstract void onLoadPackage(final SmartlyPackageLoader loader);
// ------------------------------------------------------------------------
// p r i v a t e
// ------------------------------------------------------------------------
@SuppressWarnings("unchecked")
private void runInternal() {
try {
final SmartlyPackageLoader packageLoader = new SmartlyPackageLoader();
// on-before start
this.onLoadPackage(packageLoader);
// run smartly runner
_runnerClass.getMethod("run", SmartlyPackageLoader.class).invoke(_runnerInstance, packageLoader);
} catch (Exception x) {
System.err.println("Uncaught exception: ");
x.printStackTrace();
System.exit(2);
}
}
private ClassLoader init(final String[] args) throws Exception {
// init internal logger
LoggingRepository.getInstance().setFilePath(IConstants.PATH_LOG.concat("/smartly.log"));
// read args
this.parseArgs(args);
// creates directories
this.initDirs();
final File home = getSmartlyHome();
final ClassLoader loader = createClassLoader(home);
return loader;
}
private void initDirs() throws Exception {
FileUtils.mkdirs(PathUtils.getAbsolutePath(IConstants.PATH_LOG));
FileUtils.mkdirs(PathUtils.getAbsolutePath(IConstants.PATH_CONFIGFILES));
FileUtils.mkdirs(PathUtils.getAbsolutePath(IConstants.PATH_LIBRARIES));
FileUtils.mkdirs(PathUtils.getAbsolutePath(IConstants.PATH_PACKAGES));
}
private void parseArgs(final String[] args) throws
CmdLineParser.UnknownOptionException,
CmdLineParser.IllegalOptionValueException {
if (null != args && args.length > 0) {
final CmdLineParser parser = new CmdLineParser();
final CmdLineParser.Option workspaceOpt = parser.addStringOption('w', "workspace");
final CmdLineParser.Option charsetOpt = parser.addStringOption('c', "charset");
final CmdLineParser.Option testOpt = parser.addBooleanOption('t', "test"); // test mode (only for test unit)
final CmdLineParser.Option adminOpt = parser.addBooleanOption('a', "admin"); // admin mode (only for administration mode)
final CmdLineParser.Option proxyOpt = parser.addBooleanOption('p', "proxy"); // proxy (-p true, -p false) if true uses system proxy
parser.parse(args);
String workspace = (String) parser.getOptionValue(workspaceOpt);
if (null != workspace) {
if (workspace.contains(APPDIR)) {
workspace = this.replaceInPath(workspace, APPDIR, IConstants.USER_DIR);
} else if (workspace.contains(USERHOME)) {
workspace = this.replaceInPath(workspace, USERHOME, IConstants.USER_HOME);
}
System.setProperty(IConstants.SYSPROP_HOME, workspace);
_argsMap.put("w", workspace);
}
final String charset = (String) parser.getOptionValue(charsetOpt, CharEncoding.getDefault());
if (null != charset) {
CharEncoding.setDefault(charset);
_argsMap.put("c", charset);
}
final boolean test = (Boolean) parser.getOptionValue(testOpt, false);
_argsMap.put("t", test);
final boolean admin = (Boolean) parser.getOptionValue(adminOpt, false);
_argsMap.put("a", admin);
// set default proxy for outgoing communications
final boolean use_proxy = (Boolean) parser.getOptionValue(proxyOpt, false);
_argsMap.put("p", use_proxy);
System.setProperty("java.net.useSystemProxies", use_proxy + ""); // java.net property
System.setProperty(IConstants.SYSPROP_USE_PROXIES, use_proxy + ""); // smartly property
//-- remaining --//
_argsRemain = parser.getRemainingArgs();
if (null != _argsRemain && _argsRemain.length > 0) {
for (int i = 0; i < _argsRemain.length; i++) {
_argsMap.put("param_" + i, _argsRemain[i]);
}
}
}
}
private String replaceInPath(final String path, final String what, final String with) {
final String result = StringUtils.replace(path, what, with);
return PathUtils.toUnixPath(result);
}
// ------------------------------------------------------------------------
// S T A T I C
// ------------------------------------------------------------------------
/**
* Create a server-wide ClassLoader from our install directory.
* This will be used as parent ClassLoader for all application
* ClassLoaders.
*
* @param home the ringo install directory
* @return the main classloader we'll be using
* @throws java.net.MalformedURLException
*/
private static ClassLoader createClassLoader(final File home)
throws IOException {
final String classpath = System.getProperty(SYSPROP_CLASSPATH, DEFAULT_CLASSPATH);
final String[] classes = classpath.split(",");
// ensure classpath exists
for (final String path : classes) {
final String cleanPath = path.replaceAll("\\*", "");
FileUtils.mkdirs(PathUtils.getAbsolutePath(cleanPath));
}
// creates and set the new class loader as context class loader
final SmartlyClassLoader loader = new SmartlyClassLoader(home, classes);
Thread.currentThread().setContextClassLoader(loader);
return loader;
}
/**
* Get the Smartly install directory.
*
* @return the base install directory we're running in
* @throws IOException an I/O related exception occurred
* @throws java.net.MalformedURLException the jar URL couldn't be parsed
*/
private static File getSmartlyHome()
throws IOException {
// check if home directory is set via system property
String smartlyHome = System.getProperty(IConstants.SYSPROP_HOME);
if (!StringUtils.hasText(smartlyHome)) {
smartlyHome = System.getenv(ENVPROP_HOME);
}
if (!StringUtils.hasText(smartlyHome)) {
URL launcherUrl = findUrl(SmartlyLauncher.class.getClassLoader());
if (launcherUrl == null) {
launcherUrl = findUrl(Thread.currentThread().getContextClassLoader());
}
if (launcherUrl == null) {
launcherUrl = findUrl(ClassLoader.getSystemClassLoader());
}
// this is a JAR URL of the form
// jar:<url>!/{entry}
// we strip away the jar: prefix and the !/{entry} suffix
// to get the original jar file URL
String jarUrl = launcherUrl.toString();
// decode installDir in case it is URL-encoded
try {
jarUrl = URLDecoder.decode(jarUrl, System.getProperty("file.encoding"));
} catch (UnsupportedEncodingException x) {
System.err.println("Unable to decode jar URL: " + x);
}
if (!jarUrl.startsWith("jar:") || !jarUrl.contains("!")) {
smartlyHome = IConstants.USER_DIR; //System.getProperty("user.dir");
System.err.println("Warning: smartly.home system property is not set ");
System.err.println(" and not started from launcher.jar. Using ");
System.err.println(" current working directory as install dir.");
} else {
int excl = jarUrl.indexOf("!");
jarUrl = jarUrl.substring(4, excl);
launcherUrl = new URL(jarUrl);
smartlyHome = new File(launcherUrl.getPath()).getParent();
if (smartlyHome == null) {
smartlyHome = IConstants.USER_DIR;
}
}
}
final File home = new File(smartlyHome).getCanonicalFile();
// set System property (this must be called into main)
System.setProperty(IConstants.SYSPROP_HOME, home.getPath());
return home;
}
private static URL findUrl(ClassLoader loader) {
if (loader instanceof URLClassLoader) {
return ((URLClassLoader) loader).findResource("org/smartly/launcher/SmartlyLauncher.class");
}
return null;
}
}