• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.build;
18 
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.core.runtime.IStatus;
23 import org.eclipse.core.runtime.Status;
24 
25 import java.io.File;
26 import java.io.PrintStream;
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.Field;
29 import java.lang.reflect.Method;
30 import java.net.MalformedURLException;
31 import java.net.URL;
32 import java.net.URLClassLoader;
33 
34 /**
35  * Wrapper to access dx.jar through reflection.
36  * <p/>Since there is no proper api to call the method in the dex library, this wrapper is going
37  * to access it through reflection.
38  */
39 public final class DexWrapper {
40 
41     private final static String DEX_MAIN = "com.android.dx.command.dexer.Main"; //$NON-NLS-1$
42     private final static String DEX_CONSOLE = "com.android.dx.command.DxConsole"; //$NON-NLS-1$
43     private final static String DEX_ARGS = "com.android.dx.command.dexer.Main$Arguments"; //$NON-NLS-1$
44 
45     private final static String MAIN_RUN = "run"; //$NON-NLS-1$
46 
47     private Method mRunMethod;
48 
49     private Constructor<?> mArgConstructor;
50     private Field mArgOutName;
51     private Field mArgVerbose;
52     private Field mArgJarOutput;
53     private Field mArgFileNames;
54 
55     private Field mConsoleOut;
56     private Field mConsoleErr;
57 
58     /**
59      * Loads the dex library from a file path.
60      *
61      * The loaded library can be used via
62      * {@link DexWrapper#run(String, String[], boolean, PrintStream, PrintStream)}.
63      *
64      * @param osFilepath the location of the dx.jar file.
65      * @return an IStatus indicating the result of the load.
66      */
loadDex(String osFilepath)67     public synchronized IStatus loadDex(String osFilepath) {
68         try {
69             File f = new File(osFilepath);
70             if (f.isFile() == false) {
71                 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, String.format(
72                         Messages.DexWrapper_s_does_not_exists, osFilepath));
73             }
74             URL url = f.toURI().toURL();
75 
76             URLClassLoader loader = new URLClassLoader(new URL[] { url },
77                     DexWrapper.class.getClassLoader());
78 
79             // get the classes.
80             Class<?> mainClass = loader.loadClass(DEX_MAIN);
81             Class<?> consoleClass = loader.loadClass(DEX_CONSOLE);
82             Class<?> argClass = loader.loadClass(DEX_ARGS);
83 
84             try {
85                 // now get the fields/methods we need
86                 mRunMethod = mainClass.getMethod(MAIN_RUN, argClass);
87 
88                 mArgConstructor = argClass.getConstructor();
89                 mArgOutName = argClass.getField("outName"); //$NON-NLS-1$
90                 mArgJarOutput = argClass.getField("jarOutput"); //$NON-NLS-1$
91                 mArgFileNames = argClass.getField("fileNames"); //$NON-NLS-1$
92                 mArgVerbose = argClass.getField("verbose"); //$NON-NLS-1$
93 
94                 mConsoleOut = consoleClass.getField("out"); //$NON-NLS-1$
95                 mConsoleErr = consoleClass.getField("err"); //$NON-NLS-1$
96 
97             } catch (SecurityException e) {
98                 return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_API, e);
99             } catch (NoSuchMethodException e) {
100                 return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Method, e);
101             } catch (NoSuchFieldException e) {
102                 return createErrorStatus(Messages.DexWrapper_SecuryEx_Unable_To_Find_Field, e);
103             }
104 
105             return Status.OK_STATUS;
106         } catch (MalformedURLException e) {
107             // really this should not happen.
108             return createErrorStatus(
109                     String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
110         } catch (ClassNotFoundException e) {
111             return createErrorStatus(
112                     String.format(Messages.DexWrapper_Failed_to_load_s, osFilepath), e);
113         }
114     }
115 
116     /**
117      * Removes any reference to the dex library.
118      * <p/>
119      * {@link #loadDex(String)} must be called on the wrapper
120      * before {@link #run(String, String[], boolean, PrintStream, PrintStream)} can
121      * be used again.
122      */
unload()123     public synchronized void unload() {
124         mRunMethod = null;
125         mArgConstructor = null;
126         mArgOutName = null;
127         mArgJarOutput = null;
128         mArgFileNames = null;
129         mArgVerbose = null;
130         mConsoleOut = null;
131         mConsoleErr = null;
132         System.gc();
133     }
134 
135     /**
136      * Runs the dex command.
137      * The wrapper must have been initialized via {@link #loadDex(String)} first.
138      *
139      * @param osOutFilePath the OS path to the outputfile (classes.dex
140      * @param osFilenames list of input source files (.class and .jar files)
141      * @param verbose verbose mode.
142      * @param outStream the stdout console
143      * @param errStream the stderr console
144      * @return the integer return code of com.android.dx.command.dexer.Main.run()
145      * @throws CoreException
146      */
run(String osOutFilePath, String[] osFilenames, boolean verbose, PrintStream outStream, PrintStream errStream)147     public synchronized int run(String osOutFilePath, String[] osFilenames,
148             boolean verbose, PrintStream outStream, PrintStream errStream) throws CoreException {
149 
150         assert mRunMethod != null;
151         assert mArgConstructor != null;
152         assert mArgOutName != null;
153         assert mArgJarOutput != null;
154         assert mArgFileNames != null;
155         assert mArgVerbose != null;
156         assert mConsoleOut != null;
157         assert mConsoleErr != null;
158 
159         if (mRunMethod == null) {
160             throw new CoreException(createErrorStatus(
161                     String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s,
162                             "wrapper was not properly loaded first"),
163                     null /*exception*/));
164         }
165 
166         try {
167             // set the stream
168             mConsoleErr.set(null /* obj: static field */, errStream);
169             mConsoleOut.set(null /* obj: static field */, outStream);
170 
171             // create the Arguments object.
172             Object args = mArgConstructor.newInstance();
173             mArgOutName.set(args, osOutFilePath);
174             mArgFileNames.set(args, osFilenames);
175             mArgJarOutput.set(args, false);
176             mArgVerbose.set(args, verbose);
177 
178             // call the run method
179             Object res = mRunMethod.invoke(null /* obj: static method */, args);
180 
181             if (res instanceof Integer) {
182                 return ((Integer)res).intValue();
183             }
184 
185             return -1;
186         } catch (Exception e) {
187             Throwable t = e;
188             while (t.getCause() != null) {
189                 t = t.getCause();
190             }
191 
192             String msg = t.getMessage();
193             if (msg == null) {
194                 msg = String.format("%s. Check the Eclipse log for stack trace.",
195                         t.getClass().getName());
196             }
197 
198             throw new CoreException(createErrorStatus(
199                     String.format(Messages.DexWrapper_Unable_To_Execute_Dex_s, msg), t));
200         }
201     }
202 
createErrorStatus(String message, Throwable e)203     private static IStatus createErrorStatus(String message, Throwable e) {
204         AdtPlugin.log(e, message);
205         AdtPlugin.printErrorToConsole(Messages.DexWrapper_Dex_Loader, message);
206 
207         return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message, e);
208     }
209 }
210