package org.ruboto; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.UndeclaredThrowableException; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import com.android.dx.Version; import com.headius.android.dex.DexClient; import dalvik.system.DexClassLoader; public class DalvikProxyClassFactory extends org.jruby.javasupport.proxy.JavaProxyClassFactory { private static final String DEX_IN_JAR_NAME = "classes.dex"; private static final Attributes.Name CREATED_BY = new Attributes.Name("Created-By"); public Class invokeDefineClass(ClassLoader loader, String className, byte[] data) { String cachePath = System.getProperty("jruby.class.cache.path"); if (cachePath != null) { byte[] dalvikByteCode = new DexClient().classesToDex( new String[] { className.replace('.', '/') + ".class" }, new byte[][] { data }); String jarFileName = cachePath + "/" + className.replace('.', '/') + ".jar"; createJar(jarFileName, dalvikByteCode); try { return new DexClassLoader(jarFileName, cachePath, null, loader) .loadClass(className); } catch (ClassNotFoundException e1) { System.out.println("Exception loading class with DexClassLoader: " + e1); e1.printStackTrace(); } } return null; } private static boolean createJar(String fileName, byte[] dexArray) { File parentFile = new File(fileName).getParentFile(); if (!parentFile.exists()) { System.out.println("Creating directory: " + parentFile); parentFile.mkdirs(); } try { TreeMap outputResources = new TreeMap(); Manifest manifest = makeManifest(); OutputStream out = (fileName.equals("-") || fileName.startsWith("-.")) ? System.out : new FileOutputStream(fileName); JarOutputStream jarOut = new JarOutputStream(out, manifest); outputResources.put(DEX_IN_JAR_NAME, dexArray); try { for (Map.Entry e : outputResources.entrySet()) { String name = e.getKey(); byte[] contents = e.getValue(); JarEntry entry = new JarEntry(name); entry.setSize(contents.length); jarOut.putNextEntry(entry); jarOut.write(contents); jarOut.closeEntry(); } } finally { jarOut.finish(); jarOut.flush(); if (out != null) { out.flush(); if (out != System.out) { out.close(); } } jarOut.close(); } } catch (Exception ex) { System.out.println("Exception writing jar: " + fileName); System.out.println("Exception writing jar: " + ex); ex.printStackTrace(); return false; } return true; } private static Manifest makeManifest() throws IOException { Manifest manifest = new Manifest(); Attributes attribs = manifest.getMainAttributes(); attribs.put(Attributes.Name.MANIFEST_VERSION, "1.0"); attribs.put(CREATED_BY, "dx " + Version.VERSION); attribs.putValue("Dex-Location", DEX_IN_JAR_NAME); return manifest; } }