オレオレHotReloading
以前から興味があったHotReloadingを実装してみた。
全然完璧じゃないけど。
とりあえずGitHubあげてる。
で、やってみた感想ですが、バヤい。これはバヤい。
かなりハマる。だだハマりした。
とりあえず軽くハマった点。
「Java言語仕様とJava仮想マシン仕様は軽くでも読んでおいたほうがよい」
これに尽きる。
- どうやってJVMがクラスをローディングするの?
- セキュリティーってどうなってんの?
これが分かれば(まだ分かってないけど)HotReloadingも夢じゃないはず。
ソースコードは以下のようなこんな感じ。
■試しかた。
Mainを実行すると入力待ちになり、Enterを押すとhello.Sample#executeが実行される。
それが延々と繰り返されるので入力待ちの間にexecuteメソッドを変更すると、executeから出力される内容が変わる。
package hello; import hello.classloader.CustomClassLoader; import java.io.BufferedReader; import java.io.InputStreamReader; import java.lang.reflect.Method; public class Main { public static void main(String[] args) throws Exception { Main main = new Main(); main.run(); } public void run() throws Exception { while (true) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); // Initialize ClassLoader CustomClassLoader loader = new CustomClassLoader(); System.out.println(">>> Please hit the Enter key."); reader.readLine(); String input = "hello.Sample execute"; String[] str = input.split(" "); String className = str[0]; String methodName = str[1]; System.out.println("===== " + input + " ====="); Class<?> clazz = loader.loadClass(className); Object newInstance = clazz.newInstance(); Method method = clazz.getDeclaredMethod(methodName); method.invoke(newInstance); System.out.println("<<<"); System.out.println(); } } }
package hello; public class Sample { public void execute(){ System.out.println("Hello Hot Reloading!!"); } }
package hello.classloader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; public class CustomClassLoader extends ClassLoader { @Override public Class<?> loadClass(String className) throws ClassNotFoundException { if(className.startsWith("java")){ return Class.forName(className); } int idx = className.lastIndexOf('.'); if (idx >= 0) { // Defines the package String packageName = className.substring(0, idx); if (getPackage(packageName) == null) { try { definePackage(packageName, null, null, null, null, null,null, null); } catch (IllegalArgumentException ignore) { } } Class<?> clazz = defineClass(className); if (clazz != null) { return clazz; } } return super.loadClass(className); } protected Class<?> defineClass(String className) { Class<?> clazz; String path = className.replace('.', '/') + ".class"; InputStream is = getInputStream(path); if (is != null) { byte[] bytes = getBytes(is); clazz = defineClass(className, bytes, 0, bytes.length); resolveClass(clazz); return clazz; } return null; } protected InputStream getInputStream(String path) { try { URL url = getResource(path); if (url == null) { return null; } URLConnection connection = url.openConnection(); connection.setUseCaches(false); return connection.getInputStream(); } catch (IOException ignore) { return null; } } protected byte[] getBytes(InputStream is) { byte[] bytes = null; byte[] buf = new byte[8192]; try { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int n = 0; while ((n = is.read(buf, 0, buf.length)) != -1) { baos.write(buf, 0, n); } bytes = baos.toByteArray(); } finally { if (is != null) { is.close(); } } } catch (IOException ignore) { } return bytes; } }
クラスローダーはSlim3を参考にしまくって一部そのままのメソッドもあり〼。
とりあえずSlim3すごい。読むのと書くのじゃ大違い。
以下、参考までに。
【クラスローディングの仕組み】
http://java.sun.com/docs/books/jls/third_edition/html/execution.html
http://www.ibm.com/developerworks/jp/websphere/library/java/j2ee_classloader/1.html
【Javaのセキュリティー】
http://java.sun.com/javase/ja/6/docs/ja/technotes/guides/security/spec/security-spec.doc1.html#18313
http://java.sun.com/javase/ja/6/docs/ja/technotes/guides/security/spec/security-specTOC.fm.html