package com.wangyin.ak47.boot;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import com.wangyin.ak47.common.Ak47Constants;
import com.wangyin.ak47.common.Ak47Env;
import com.wangyin.ak47.common.DynamicClassLoader;
/**
* Ak47 boot entry.
* Responsible for dynamically loading all dependent jars, and extend jars.
* Then forward the parameters to the real-target class and main method.
*
* WARNING: Any other class should NOT dependent on this class.
*
* Ak47可执行jar包的入口,负责加载各种jar包,并将参数转交给真正的main方法。
* 本类不能依赖Ak47下其他子包的类。
*
* 注:任何其他类都不应该依赖此类。
*
* @author hannyu
*
*/
public class Ak47Boot {
private static final String CLASSPATH_SEPARATOR = System.getProperty("path.separator");// $NON-NLS-1$
private static final String OS_NAME = System.getProperty("os.name");// $NON-NLS-1$
private static final String OS_NAME_LC = OS_NAME.toLowerCase(java.util.Locale.ENGLISH);
private static final String JAVA_CLASS_PATH = "java.class.path";
/*
* Does the system support UNC paths? If so, may need to fix them up
* later
*/
private static final boolean usesUNC = OS_NAME_LC.startsWith("windows");// $NON-NLS-1$
private static DynamicClassLoader loader;
/**
* new DynamicClassLoader
*
* @param classpath
* @param jars
* @return
*/
private static DynamicClassLoader genLoaderAndSetClasspath(String classpath, final List<URL> jars){
System.setProperty(JAVA_CLASS_PATH, classpath);
return AccessController.doPrivileged(
new java.security.PrivilegedAction<DynamicClassLoader>() {
@Override
public DynamicClassLoader run() {
return new DynamicClassLoader(jars.toArray(new URL[jars.size()]));
}
}
);
}
/**
* 查找jarDir目录下所有jar包(不包含子目录),并拼装classpaths和jars
*
* @param jarDir
* @return
*/
private static void findAndAddClasspaths(String jarDir, StringBuilder classpaths,
List<URL> jars){
File libDir = new File(jarDir);
if( !libDir.exists() ){
return;
}
// jarDir is a file
if( libDir.isFile() ){
try {
jars.add(libDir.toURI().toURL());
classpaths.append(CLASSPATH_SEPARATOR);
classpaths.append(libDir.getPath());
} catch (MalformedURLException e) {
e.printStackTrace();
}
return;
}
// jarDir is a directory
File[] libJars = libDir.listFiles(new FilenameFilter(){
@Override
public boolean accept(File dir, String name) {
if( name.endsWith(".jar") ){ // $NON-NLS-1$
File jar = new File(dir, name);
return jar.isFile() && jar.canRead();
}
return false;
}
});
if (libJars == null) {
new Throwable("Could not access " + libDir).printStackTrace();
return;
}
Arrays.sort(libJars);
for (File libJar : libJars) {
try {
String s = libJar.getPath();
// Fix path to allow the use of UNC URLs
if (usesUNC) {
if (s.startsWith("\\\\") && !s.startsWith("\\\\\\")) {// $NON-NLS-1$ $NON-NLS-2$
s = "\\\\" + s;// $NON-NLS-1$
} else if (s.startsWith("//") && !s.startsWith("///")) {// $NON-NLS-1$ $NON-NLS-2$
s = "//" + s;// $NON-NLS-1$
}
} // usesUNC
jars.add(new File(s).toURI().toURL());// See Java bug 4496398
classpaths.append(CLASSPATH_SEPARATOR);
classpaths.append(s);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}//for
}
/**
* print usage
*/
public static void usage(){
System.out.println("AK47Boot -v | -h | [-x extension] MainClass [and their args]");
System.out.println("Example: ak47boot.sh -x iso8583 -m my.company.TestMyMock wakaka");
System.out.println("Version: " + Ak47Constants.VERSION);
System.exit(0);
}
/**
* runable entry
*
* @param args command arguments
* @throws IOException when NOT in ak47 project
*/
public static void main(String[] args) throws IOException {
// reslove args
String extension = null;
String mainclassname = null;
String[] mainargs = null;
if( args != null && args.length >= 1 ){
if( "-v".equals(args[0]) || "-h".equals(args[0]) ){
usage();
}else if( "-x".equals(args[0]) ){
if( args.length >= 3){
extension = args[1];
mainclassname = args[2];
mainargs = new String[args.length-3];
System.arraycopy(args, 3, mainargs, 0, args.length-3);
}else{
System.out.println("ERROR: Wrong arguments!");
usage();
}
}else if( args[0].startsWith("-x") ){
if( args.length >= 2){
extension = args[0].substring(2, args.length);
mainclassname = args[1];
mainargs = new String[args.length-2];
System.arraycopy(args, 2, mainargs, 0, args.length-2);
}else{
System.out.println("ERROR: Wrong arguments!");
usage();
}
}else{
mainclassname = args[0];
mainargs = new String[args.length-1];
System.arraycopy(args, 1, mainargs, 0, args.length-1);
}
}else{
usage();
}
// update extension dir
if( extension != null ){
Ak47Env.AK47_HOME_EXT_DIR = Ak47Env.AK47_HOME_EXT_DIR + extension;
}
// init loader
final List<URL> jars = new LinkedList<URL>();
final String initialClasspaths = System.getProperty(JAVA_CLASS_PATH);
final StringBuilder classpaths = new StringBuilder();
// only when running from jar
if( Ak47Env.IF_RUNNING_FROM_JAR ){
// load lib/*.jar
findAndAddClasspaths(Ak47Env.AK47_HOME_LIB_DIR, classpaths, jars);
// load ext[-extension]/*.jar
findAndAddClasspaths(Ak47Env.AK47_HOME_EXT_DIR, classpaths, jars);
}
// genarate loader
loader = genLoaderAndSetClasspath(initialClasspaths + classpaths.toString(), jars);
// need dynamic class loader
Thread.currentThread().setContextClassLoader(loader);
// set logback.configurationFile
if( null == System.getProperty("logback.configurationFile") ){
File file = new File(Ak47Env.AK47_HOME_CONF_DIR + File.separator + "logback.xml");
System.setProperty("logback.configurationFile", file.getCanonicalPath());
}
// do specified job
try {
Class<?> mainClass;
mainClass = loader.loadClass(mainclassname);
Method mainMethod = mainClass.getMethod("main", new Class[] { new String[0].getClass() });// $NON-NLS-1$
mainMethod.invoke(mainClass, new Object[] { mainargs });
} catch(Throwable e){
e.printStackTrace();
// System.err.println("AK47 home dir was detected as: " + Ak47Env.AK47_HOME_DIR);
System.exit(1);
}
System.exit(0);
}
}