ruboto/ruboto

View on GitHub
lib/DalvikProxyClassFactory.java

Summary

Maintainability
A
2 hrs
Test Coverage
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<String, byte[]> outputResources = new TreeMap<String, byte[]>();
            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<String, byte[]> 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;
    }

}