1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ide.eclipse.adt.internal.sdk; 18 19 import com.android.ide.eclipse.adt.AdtPlugin; 20 import com.android.ide.eclipse.adt.internal.build.Messages; 21 22 import org.eclipse.core.runtime.CoreException; 23 import org.eclipse.core.runtime.IStatus; 24 import org.eclipse.core.runtime.Status; 25 26 import java.io.File; 27 import java.io.PrintStream; 28 import java.lang.reflect.Constructor; 29 import java.lang.reflect.Field; 30 import java.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 32 import java.net.MalformedURLException; 33 import java.net.URL; 34 import java.net.URLClassLoader; 35 36 /** 37 * Wrapper to access dex.jar through reflection. 38 * <p/>Since there is no proper api to call the method in the dex library, this wrapper is going 39 * to access it through reflection. 40 */ 41 public final class DexWrapper { 42 43 private final static String DEX_MAIN = "com.android.dx.command.dexer.Main"; //$NON-NLS-1$ 44 private final static String DEX_CONSOLE = "com.android.dx.command.DxConsole"; //$NON-NLS-1$ 45 private final static String DEX_ARGS = "com.android.dx.command.dexer.Main$Arguments"; //$NON-NLS-1$ 46 47 private final static String MAIN_RUN = "run"; //$NON-NLS-1$ 48 49 private Method mRunMethod; 50 51 private Constructor<?> mArgConstructor; 52 private Field mArgOutName; 53 private Field mArgVerbose; 54 private Field mArgJarOutput; 55 private Field mArgFileNames; 56 57 private Field mConsoleOut; 58 private Field mConsoleErr; 59 60 /** 61 * Loads the dex library from a file path. 62 * 63 * The loaded library can be used via 64 * {@link DexWrapper#run(String, String[], boolean, PrintStream, PrintStream)}. 65 * 66 * @param osFilepath the location of the dex.jar file. 67 * @return an IStatus indicating the result of the load. 68 */ loadDex(String osFilepath)69 public synchronized IStatus loadDex(String osFilepath) { 70 try { 71 File f = new File(osFilepath); 72 if (f.isFile() == false) { 73 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, String.format( 74 Messages.DexWrapper_s_does_not_exists, osFilepath)); 75 } 76 URL url = f.toURL(); 77 78 URLClassLoader loader = new URLClassLoader(new URL[] { url }, 79 DexWrapper.class.getClassLoader()); 80 81 // get the classes. 82 Class<?> mainClass = loader.loadClass(DEX_MAIN); 83 Class<?> consoleClass = loader.loadClass(DEX_CONSOLE); 84 Class<?> argClass = loader.loadClass(DEX_ARGS); 85 86 try { 87 // now get the fields/methods we need 88 mRunMethod = mainClass.getMethod(MAIN_RUN, argClass); 89 90 mArgConstructor = argClass.getConstructor(); 91 mArgOutName = argClass.getField("outName"); //$NON-NLS-1$ 92 mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$ 93 mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$ 94 mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$ 95 96 mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$ 97 mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$ 98 99 } catch (SecurityException e) { 100 return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_API, e); 101 } catch (NoSuchMethodException e) { 102 return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Method, e); 103 } catch (NoSuchFieldException e) { 104 return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Field, e); 105 } 106 107 return Status.OK_STATUS; 108 } catch (MalformedURLException e) { 109 // really this should not happen. 110 return createErrorStatus( 111 String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e); 112 } catch (ClassNotFoundException e) { 113 return createErrorStatus( 114 String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e); 115 } 116 } 117 118 /** 119 * Runs the dex command. 120 * @param osOutFilePath the OS path to the outputfile (classes.dex 121 * @param osFilenames list of input source files (.class and .jar files) 122 * @param verbose verbose mode. 123 * @param outStream the stdout console 124 * @param errStream the stderr console 125 * @return the integer return code of com.android.dx.command.dexer.Main.run() 126 * @throws CoreException 127 */ run(String osOutFilePath, String[] osFilenames, boolean verbose, PrintStream outStream, PrintStream errStream)128 public synchronized int run(String osOutFilePath, String[] osFilenames, 129 boolean verbose, PrintStream outStream, PrintStream errStream) throws CoreException { 130 131 try { 132 // set the stream 133 mConsoleErr.set(null /* obj: static field */, errStream); 134 mConsoleOut.set(null /* obj: static field */, outStream); 135 136 // create the Arguments object. 137 Object args = mArgConstructor.newInstance(); 138 mArgOutName.set(args, osOutFilePath); 139 mArgFileNames.set(args, osFilenames); 140 mArgJarOutput.set(args, false); 141 mArgVerbose.set(args, verbose); 142 143 // call the run method 144 Object res = mRunMethod.invoke(null /* obj: static method */, args); 145 146 if (res instanceof Integer) { 147 return ((Integer)res).intValue(); 148 } 149 150 return -1; 151 } catch (IllegalAccessException e) { 152 throw new CoreException(createErrorStatus( 153 String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s, e.getMessage()), e)); 154 } catch (InstantiationException e) { 155 throw new CoreException(createErrorStatus( 156 String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s, e.getMessage()), e)); 157 } catch (InvocationTargetException e) { 158 throw new CoreException(createErrorStatus( 159 String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s, e.getMessage()), e)); 160 } 161 } 162 createErrorStatus(String message, Exception e)163 private static IStatus createErrorStatus(String message, Exception e) { 164 AdtPlugin.log(e, message); 165 AdtPlugin.printErrorToConsole(Messages.DexWrapper_Dex_Loader, message); 166 167 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message, e); 168 } 169 } 170