├── sample ├── test1.prop ├── test2.prop ├── test1 │ └── com │ │ └── codemacro │ │ └── test │ │ ├── Base.java │ │ └── A.java ├── test2 │ └── com │ │ └── codemacro │ │ └── test │ │ ├── Base.java │ │ ├── Export.java │ │ └── B.java └── compile.bat ├── kcontainer ├── src │ ├── main │ │ ├── resources │ │ │ └── simplelogger.properties │ │ └── java │ │ │ └── com │ │ │ └── codemacro │ │ │ └── container │ │ │ ├── Main.java │ │ │ ├── SharedClassList.java │ │ │ ├── BundleConf.java │ │ │ ├── util │ │ │ └── UnzipJar.java │ │ │ ├── Bundle.java │ │ │ ├── KContainer.java │ │ │ └── BundleClassLoader.java │ └── test │ │ └── java │ │ └── com │ │ └── codemacro │ │ └── container │ │ └── AppTest.java └── pom.xml ├── .gitignore ├── kcontainer.interface ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── codemacro │ │ │ └── container │ │ │ ├── IBundleInitializer.java │ │ │ └── BundleContext.java │ └── test │ │ └── java │ │ └── com │ │ └── codemacro │ │ └── container │ │ └── AppTest.java └── pom.xml ├── pom.xml └── README.md /sample/test1.prop: -------------------------------------------------------------------------------- 1 | export-class=com.codemacro.test.A 2 | 3 | -------------------------------------------------------------------------------- /sample/test2.prop: -------------------------------------------------------------------------------- 1 | init=com.codemacro.test.B 2 | export-class=com.codemacro.test.Export 3 | -------------------------------------------------------------------------------- /kcontainer/src/main/resources/simplelogger.properties: -------------------------------------------------------------------------------- 1 | org.slf4j.simpleLogger.defaultLogLevel=debug 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.class 3 | *target* 4 | .classpath 5 | .project 6 | .settings 7 | bundle 8 | -------------------------------------------------------------------------------- /sample/test1/com/codemacro/test/Base.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.test; 2 | 3 | public class Base { 4 | public void print() { 5 | System.out.println("test1 Base"); 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /sample/test2/com/codemacro/test/Base.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.test; 2 | 3 | public class Base { 4 | public void print() { 5 | System.out.println("test2 Base"); 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /sample/test2/com/codemacro/test/Export.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.test; 2 | 3 | public class Export { 4 | public void print() { 5 | System.out.println("test2 Export"); 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /kcontainer.interface/src/main/java/com/codemacro/container/IBundleInitializer.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | public interface IBundleInitializer { 4 | void start(BundleContext context); 5 | void stop(BundleContext context); 6 | } 7 | -------------------------------------------------------------------------------- /kcontainer/src/main/java/com/codemacro/container/Main.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | // a sample starter 4 | public class Main { 5 | 6 | public static void main(String[] args) { 7 | KContainer container = new KContainer(); 8 | container.start(); 9 | container.stop(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /kcontainer.interface/src/main/java/com/codemacro/container/BundleContext.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | public class BundleContext { 4 | private String name; 5 | 6 | public String getName() { 7 | return name; 8 | } 9 | 10 | public void setName(String name) { 11 | this.name = name; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sample/test1/com/codemacro/test/A.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.test; 2 | 3 | public class A { 4 | static { 5 | System.out.println("====A===="); 6 | System.out.println(A.class.getClassLoader()); 7 | Base b = new Base(); 8 | b.print(); 9 | Export e = new Export(); 10 | e.print(); 11 | System.out.println("====A===="); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.codemacro.container 5 | kcontainer-parent 6 | pom 7 | ${k.version} 8 | kcontainer 9 | 10 | kcontainer 11 | kcontainer.interface 12 | 13 | 14 | 0.1.0 15 | 16 | 17 | -------------------------------------------------------------------------------- /sample/test2/com/codemacro/test/B.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.test; 2 | 3 | import com.codemacro.container.IBundleInitializer; 4 | import com.codemacro.container.BundleContext; 5 | 6 | public class B implements IBundleInitializer { 7 | static { 8 | System.out.println("====B===="); 9 | Base b = new Base(); 10 | b.print(); 11 | System.out.println(B.class.getClassLoader()); 12 | System.out.println("====B===="); 13 | } 14 | 15 | public void start(BundleContext context) { 16 | System.out.println("B start: " + context.getName()); 17 | new A(); 18 | } 19 | 20 | public void stop(BundleContext context) { 21 | System.out.println("B stop: " + context.getName()); 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /kcontainer/src/test/java/com/codemacro/container/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /kcontainer.interface/src/test/java/com/codemacro/container/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /kcontainer/src/main/java/com/codemacro/container/SharedClassList.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class SharedClassList { 9 | private static Logger logger = LoggerFactory.getLogger(SharedClassList.class); 10 | private ConcurrentHashMap> classes; 11 | 12 | public SharedClassList() { 13 | classes = new ConcurrentHashMap>(); 14 | } 15 | 16 | public void put(String full_name, Class claz) { 17 | classes.put(full_name, claz); 18 | logger.debug("add shared class {}", full_name); 19 | } 20 | 21 | public Class get(String full_name) { 22 | return classes.containsKey(full_name) ? classes.get(full_name) : null; 23 | } 24 | 25 | public void clear() { 26 | classes.clear(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/compile.bat: -------------------------------------------------------------------------------- 1 | @REM test1 2 | javac test1\com\codemacro\test\*.java -d classes\test1 -cp test1\;test2\ 3 | del classes\test1\com\codemacro\test\Export.class 4 | mkdir ..\bundle\test1\ 5 | mkdir ..\bundle\test1\classes 6 | copy test1.prop ..\bundle\test1\ /Y 7 | xcopy classes\test1 ..\bundle\test1\classes\ /S /Y 8 | 9 | @REM test2 10 | javac test2\com\codemacro\test\*.java -d classes\test2 -cp test1\;test2\;..\kcontainer.interface\src\main\java\ 11 | del classes\test2\com\codemacro\container\* /Q 12 | del classes\test2\com\codemacro\test\A.class /Q 13 | jar cf test2.jar -C classes\test2 com 14 | mkdir ..\bundle\test2\ 15 | mkdir ..\bundle\test2\lib\ 16 | copy test2.prop ..\bundle\test2\ /Y 17 | copy test2.jar ..\bundle\test2\lib\ /Y 18 | 19 | @REM to kcontainer\bundle, test in eclipse 20 | mkdir ..\kcontainer\bundle\test1 21 | xcopy ..\bundle\test1 ..\kcontainer\bundle\test1\ /S /Y 22 | mkdir ..\kcontainer\bundle\test2 23 | xcopy ..\bundle\test2 ..\kcontainer\bundle\test2\ /S /Y 24 | -------------------------------------------------------------------------------- /kcontainer.interface/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | kcontainer-parent 5 | com.codemacro.container 6 | ${k.version} 7 | 8 | 4.0.0 9 | com.codemacro.container 10 | kcontainer.interface 11 | jar 12 | 1.0-SNAPSHOT 13 | kcontainer.interface 14 | http://maven.apache.org 15 | 16 | 17 | com.google.guava 18 | guava-collections 19 | r03 20 | 21 | 22 | junit 23 | junit 24 | 3.8.1 25 | test 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | kcontainer is a lightweight container sample. 2 | 3 | 1. import project into eclipse (existing maven project) 4 | 2. cd sample && compile.bat, compile test1 & test2 sample bundle and copy to `kcontainer/bundle` directory, make sure `bundle` directory exists 5 | 3. run kcontainer `Main` in eclipse 6 | 7 | ## Bundle 8 | 9 | ``` 10 | . 11 | |-- bundle 12 | |-- test1 13 | |-- test1.prop 14 | |-- lib 15 | | |-- abc.jar (if contains inner jar, unpack it) 16 | | |-- def.jar 17 | |-- classes 18 | |-- lib 19 | ``` 20 | 21 | ## Bundle Initializer sample 22 | 23 | ``` 24 | public class B implements IBundleInitializer { 25 | static { 26 | System.out.println("====B===="); 27 | Base b = new Base(); 28 | b.print(); 29 | System.out.println(B.class.getClassLoader()); 30 | System.out.println("====B===="); 31 | } 32 | 33 | public void start(BundleContext context) { 34 | System.out.println("B start: " + context.getName()); 35 | new A(); 36 | } 37 | 38 | public void stop(BundleContext context) { 39 | System.out.println("B stop: " + context.getName()); 40 | } 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /kcontainer/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | kcontainer-parent 5 | com.codemacro.container 6 | ${k.version} 7 | 8 | 4.0.0 9 | com.codemacro.container 10 | kcontainer 11 | jar 12 | 1.0-SNAPSHOT 13 | kcontainer 14 | http://maven.apache.org 15 | 16 | 17 | com.codemacro.container 18 | kcontainer.interface 19 | ${project.version} 20 | 21 | 22 | org.slf4j 23 | slf4j-api 24 | 1.7.12 25 | 26 | 27 | org.slf4j 28 | slf4j-simple 29 | 1.7.5 30 | 31 | 32 | com.google.guava 33 | guava-collections 34 | r03 35 | 36 | 37 | junit 38 | junit 39 | 3.8.1 40 | test 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /kcontainer/src/main/java/com/codemacro/container/BundleConf.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.util.List; 7 | import java.util.Properties; 8 | 9 | import com.google.common.base.Function; 10 | import com.google.common.collect.Lists; 11 | 12 | // bundle properties, located at `bundle-name/name.prop` 13 | public class BundleConf { 14 | private static final String KEY_INIT = "init"; 15 | private static final String KEY_EXPORT_CLASS = "export-class"; 16 | private static final String KEY_IMPORT_CLASS = "import-class"; 17 | private List exportClassNames; 18 | private List importClassNames; 19 | private String initClassName; 20 | 21 | public BundleConf(File home) { 22 | String name = home.getName(); 23 | File prop = new File(home.getAbsolutePath() + File.separator + name + ".prop"); 24 | if (!prop.exists()) { 25 | throw new IllegalArgumentException("bundle prop file not exist"); 26 | } 27 | parse(prop); 28 | } 29 | 30 | public List getExportClassNames() { 31 | return exportClassNames; 32 | } 33 | 34 | public List getImportClassNames() { 35 | return importClassNames; 36 | } 37 | 38 | public String getInitClassName() { 39 | return initClassName; 40 | } 41 | 42 | private void parse(File prop) { 43 | Properties properties = new Properties(); 44 | try { 45 | properties.load(new FileInputStream(prop)); 46 | 47 | } catch (IOException e) { 48 | throw new IllegalArgumentException("parse bundle file failed"); 49 | } 50 | this.initClassName = properties.getProperty(KEY_INIT); 51 | this.exportClassNames = parseList(KEY_EXPORT_CLASS, properties); 52 | this.importClassNames = parseList(KEY_IMPORT_CLASS, properties); 53 | } 54 | 55 | private List parseList(String key, Properties properties) { 56 | String val = properties.getProperty(key); 57 | return val == null ? null : Lists.transform(Lists.newArrayList(val.split(";")), 58 | new Function() { 59 | public String apply(String from) { 60 | return from.trim(); 61 | } 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /kcontainer/src/main/java/com/codemacro/container/util/UnzipJar.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container.util; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.util.jar.JarEntry; 9 | import java.util.jar.JarFile; 10 | import java.util.Enumeration; 11 | import java.util.LinkedList; 12 | import java.util.List; 13 | 14 | public class UnzipJar { 15 | private static int DEFAULT_BUFFER_SIZE = 4 * 1024; 16 | 17 | // unzipJar("../tmp/jcm", "../bundle/jcm/lib/jcm.server-0.1.0.jar", ".jar"); 18 | public static List unzipJar(String destinationDir, String jarPath, String suffix) throws IOException { 19 | File file = new File(jarPath); 20 | JarFile jar = new JarFile(file); 21 | List results = new LinkedList(); 22 | for (Enumeration enums = jar.entries(); enums.hasMoreElements();) { 23 | JarEntry entry = (JarEntry) enums.nextElement(); 24 | String fileName = destinationDir + File.separator + entry.getName(); 25 | File f = new File(fileName); 26 | if (!entry.isDirectory() && entry.getName().endsWith(suffix)) { 27 | f.getParentFile().mkdirs(); 28 | InputStream is = jar.getInputStream(entry); 29 | FileOutputStream fos = new FileOutputStream(f); 30 | try { 31 | copyStream(is, fos); 32 | results.add(f); 33 | } finally { 34 | fos.close(); 35 | is.close(); 36 | } 37 | } 38 | } 39 | jar.close(); 40 | return results; 41 | } 42 | 43 | public static boolean hasEntry(String jarPath, String suffix) { 44 | JarFile jar; 45 | boolean ret = false; 46 | try { 47 | jar = new JarFile(jarPath); 48 | for (Enumeration enums = jar.entries(); enums.hasMoreElements();) { 49 | JarEntry entry = (JarEntry) enums.nextElement(); 50 | if (entry.getName().endsWith(suffix)) { 51 | ret = true; 52 | break; 53 | } 54 | } 55 | jar.close(); 56 | } catch (IOException e) { 57 | return false; 58 | } 59 | return ret; 60 | } 61 | 62 | private static void copyStream(InputStream is, OutputStream out) throws IOException { 63 | byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; 64 | int n = 0; 65 | while (-1 != (n = is.read(buffer))) { 66 | out.write(buffer, 0, n); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kcontainer/src/main/java/com/codemacro/container/Bundle.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | public class Bundle { 10 | private static Logger logger = LoggerFactory.getLogger(Bundle.class); 11 | private ClassLoader claz_loader; 12 | private String name; 13 | private String initClass; 14 | private IBundleInitializer initializer; 15 | 16 | public Bundle(String name, String initClass, ClassLoader loader) { 17 | this.name = name; 18 | this.initClass = initClass; 19 | this.claz_loader = loader; 20 | } 21 | 22 | public void start(BundleContext context) { 23 | if (initClass == null || initClass.isEmpty()) { 24 | logger.info("bundle {} has no init class", name); 25 | return ; 26 | } 27 | try { 28 | Class claz = claz_loader.loadClass(initClass); 29 | initializer = (IBundleInitializer) claz.newInstance(); 30 | initializer.start(context); 31 | logger.info("bundle {} start done", name); 32 | } catch (Exception e) { 33 | logger.warn("bundle {} init failed {}", name, e); 34 | } 35 | } 36 | 37 | public void stop(BundleContext context) { 38 | if (initializer == null) return ; 39 | initializer.stop(context); 40 | logger.info("bundle {} stop done", name); 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public ClassLoader getClassLoader() { 48 | return claz_loader; 49 | } 50 | 51 | public static Bundle create(File home, String name, SharedClassList sharedClasses, 52 | BundleConf conf) { 53 | BundleClassLoader loader = new BundleClassLoader(home, sharedClasses); 54 | List exports = conf.getExportClassNames(); 55 | if (exports != null) { 56 | logger.info("load exported classes for {}", name); 57 | loadExports(loader, sharedClasses, exports); 58 | } 59 | return new Bundle(name, conf.getInitClassName(), loader); 60 | } 61 | 62 | private static void loadExports(ClassLoader loader, SharedClassList sharedClasses, 63 | List exports) { 64 | for (String claz_name: exports) { 65 | try { 66 | Class claz = loader.loadClass(claz_name); 67 | sharedClasses.put(claz_name, claz); 68 | } catch (ClassNotFoundException e) { 69 | logger.warn("load class {} failed", claz_name); 70 | } 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /kcontainer/src/main/java/com/codemacro/container/KContainer.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | import java.io.File; 4 | import java.io.FileFilter; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /* 11 | * kcontainer directory: 12 | * -- lib 13 | * -- bundle 14 | * |-- app 15 | * |-- bundle.prop 16 | * |-- lib 17 | * 18 | */ 19 | public class KContainer { 20 | private static Logger logger = LoggerFactory.getLogger(KContainer.class); 21 | private static final String BUNDLE_PATH = "bundle"; 22 | private SharedClassList sharedClasses; 23 | private Map bundles; 24 | 25 | public KContainer() { 26 | this.sharedClasses = new SharedClassList(); 27 | this.bundles = new HashMap(); 28 | } 29 | 30 | public boolean start() { 31 | loadBundles(); 32 | startBundles(); 33 | return true; 34 | } 35 | 36 | public void stop() { 37 | stopBundles(); 38 | sharedClasses.clear(); 39 | bundles.clear(); 40 | } 41 | 42 | public Map getBundles() { 43 | return bundles; 44 | } 45 | 46 | private void loadBundles() { 47 | File root = new File(BUNDLE_PATH); 48 | logger.info("load bundles..."); 49 | File[] bundleDirs = root.listFiles(new FileFilter() { 50 | public boolean accept(File file) { 51 | return file.isDirectory(); 52 | } 53 | }); 54 | if (bundleDirs == null) { 55 | logger.error("bundle path not exists"); 56 | return ; 57 | } 58 | for (File f: bundleDirs) { 59 | Bundle bundle = loadBundle(f); 60 | if (bundle != null) { 61 | logger.info("load bundle [{}] done", bundle.getName()); 62 | bundles.put(bundle.getName(), bundle); 63 | } 64 | } 65 | } 66 | 67 | private Bundle loadBundle(File home) { 68 | try { 69 | BundleConf bundle_conf = new BundleConf(home); 70 | Bundle bundle = Bundle.create(home, home.getName(), sharedClasses, bundle_conf); 71 | return bundle; 72 | } catch (IllegalArgumentException e) { 73 | logger.warn("load bundle [{}] failed", home.getName()); 74 | return null; 75 | } 76 | } 77 | 78 | private void startBundles() { 79 | logger.info("start all bundles"); 80 | for (Bundle bundle : bundles.values()) { 81 | BundleContext context = new BundleContext(); 82 | context.setName(bundle.getName()); 83 | bundle.start(context); 84 | } 85 | } 86 | 87 | private void stopBundles() { 88 | logger.info("stop all bundles"); 89 | for (Bundle bundle : bundles.values()) { 90 | BundleContext context = new BundleContext(); 91 | context.setName(bundle.getName()); 92 | bundle.stop(context); 93 | } 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /kcontainer/src/main/java/com/codemacro/container/BundleClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.codemacro.container; 2 | 3 | import java.io.File; 4 | import java.io.FilenameFilter; 5 | import java.io.IOException; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | import java.net.URLClassLoader; 9 | import java.util.Collection; 10 | import java.util.List; 11 | 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import com.codemacro.container.util.UnzipJar; 16 | import com.google.common.base.Function; 17 | import com.google.common.base.Predicate; 18 | import com.google.common.collect.Collections2; 19 | import com.google.common.collect.Lists; 20 | 21 | // bundle-name/classes 22 | // bundle-name/lib/*.jar 23 | // bundle-name/lib/*.jar (with inner jars) 24 | public class BundleClassLoader extends URLClassLoader { 25 | private static Logger logger = LoggerFactory.getLogger(BundleClassLoader.class); 26 | private static final String TMP_PATH = new File("./tmp").getAbsolutePath(); 27 | private SharedClassList sharedClasses; 28 | 29 | public BundleClassLoader(File home, SharedClassList sharedClasses) { 30 | super(getClassPath(home)); 31 | this.sharedClasses = sharedClasses; 32 | } 33 | 34 | @Override 35 | protected Class findClass(String name) throws ClassNotFoundException { 36 | logger.debug("try find class {}", name); 37 | Class claz = null; 38 | try { 39 | claz = super.findClass(name); 40 | } catch (ClassNotFoundException e) { 41 | claz = null; 42 | } 43 | if (claz != null) { 44 | logger.debug("load from class path for {}", name); 45 | return claz; 46 | } 47 | claz = sharedClasses.get(name); 48 | if (claz != null) { 49 | logger.debug("load from shared class for {}", name); 50 | return claz; 51 | } 52 | logger.warn("not found class {}", name); 53 | throw new ClassNotFoundException(name); 54 | } 55 | 56 | private static URL[] getClassPath(File home) { 57 | File lib = new File(home.getAbsoluteFile() + "/lib"); 58 | File[] jars = lib.listFiles(new FilenameFilter() { 59 | public boolean accept(File file, String name) { 60 | return name.endsWith(".jar"); 61 | } 62 | }); 63 | Function tran_fn = new Function() { 64 | public URL apply(File file) { 65 | String s = file.isFile() ? "file:/" + file.getAbsolutePath() : 66 | "file:/" + file.getAbsolutePath() + "/"; 67 | try { 68 | return new URL(s); 69 | } catch (MalformedURLException e) { 70 | logger.warn("URL exception {}", e); 71 | return null; 72 | } 73 | } 74 | }; 75 | Collection col_urls = Lists.newArrayList(); 76 | if (jars != null) { 77 | col_urls.addAll(Lists.transform(Lists.newArrayList(jars), tran_fn)); 78 | for (File jar: jars) { // add inner jar to class path 79 | if (UnzipJar.hasEntry(jar.getAbsolutePath(), ".jar")) { 80 | List inner_jars = extractInnerJars(home.getName(), jar.getAbsolutePath()); 81 | if (inner_jars != null) { 82 | col_urls.addAll(Lists.transform(inner_jars, tran_fn)); 83 | } 84 | } 85 | } 86 | } 87 | String classes_path = home.getAbsolutePath() + "/classes/"; 88 | File classes = new File(classes_path); 89 | if (classes.exists()) { 90 | col_urls.add(tran_fn.apply(classes)); 91 | } 92 | 93 | URL[] urls = new URL[col_urls.size()]; 94 | return Lists.newArrayList(Collections2.filter(col_urls, new Predicate() { 95 | public boolean apply(URL url) { 96 | return url != null; 97 | } 98 | })).toArray(urls); 99 | } 100 | 101 | private static List extractInnerJars(String name, String jarFile) { 102 | logger.debug("extract inner jars {}", jarFile); 103 | String dst = TMP_PATH + File.separator + name; 104 | try { 105 | return UnzipJar.unzipJar(dst, jarFile, ".jar"); 106 | } catch (IOException e) { 107 | logger.warn("extract jar file {} failed", jarFile); 108 | } 109 | return null; 110 | } 111 | 112 | public static void main(String[] args) throws ClassNotFoundException, IOException, InterruptedException { 113 | File jcm = new File("bundle/jcm"); 114 | for (URL u: BundleClassLoader.getClassPath(jcm)) { 115 | System.err.println(u); 116 | } 117 | BundleClassLoader loader = new BundleClassLoader(jcm, null); 118 | System.out.println(loader.loadClass("com.codemacro.jcm.util.Hash")); 119 | System.out.println(loader.loadClass("com.codemacro.jcm.JCMMain")); 120 | loader.close(); 121 | } 122 | } 123 | --------------------------------------------------------------------------------