1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.xwalk.core.internal;
7 import java.lang.reflect.Constructor;
8 import java.lang.reflect.InvocationTargetException;
9 import java.lang.reflect.Method;
11 import java.util.HashMap;
13 import android.content.Context;
14 import android.content.pm.PackageManager;
17 * This class is used to encapsulate the reflection invoking for bridge and wrapper.
19 * org.xwalk.core.internal.ReflectionHelper is for bridge to use. A copy will be
20 * made to org.xwalk.core.ReflectionHelper at build time for wrapper to use.
22 public class ReflectionHelper {
24 * This class contains the identical information of a constructor.
26 * Constructor of bridge and wrapper class will be initialized in the
28 * It might happen before the RefletionHelper itself being initialized.
29 * For those cases, record the information of the Constructor and load
30 * them after ReflectionHelper initialized.
32 static class ConstructorHelper {
33 private String fullClassName;
34 private Object[] paramTypes;
36 Constructor<?> loadConstructor() {
37 Class<?> clazz = loadClass(fullClassName);
38 if (clazz == null) return null;
39 Class<?>[] params = new Class<?>[paramTypes.length];
40 for (int i = 0; i < paramTypes.length; i++) {
41 Object type = paramTypes[i];
42 // paramTypes can be string or Class<?>, if it's string,
43 // it means it's not in bridge/wrapper's classLoader,
44 // we need to load it from wrapper/bridge's loader.
45 if (type instanceof Class<?>) {
46 params[i] = (Class<?>) type;
47 } else if (type instanceof String) {
48 params[i] = loadClass((String) type);
52 return clazz.getConstructor(params);
53 } catch (NoSuchMethodException e) {
54 ReflectionHelper.handleException(e);
59 ConstructorHelper(String className, Object... paramTypes) {
60 this.fullClassName = className;
61 this.paramTypes = paramTypes;
65 private static Map<Class<?>, Method> sBridgeWrapperMap = new HashMap<Class<?>, Method>();
66 private static Map<String, Constructor<?>> sConstructorMap = new HashMap<String, Constructor<?>>();
67 private static Map<String, ConstructorHelper> sConstructorHelperMap =
68 new HashMap<String, ConstructorHelper>();
69 private static ClassLoader sBridgeOrWrapperLoader = null;
70 private static Context sBridgeContext = null;
71 private static boolean sIsWrapper;
72 private final static String INTERNAL_PACKAGE = "org.xwalk.core.internal";
73 private final static String LIBRARY_APK_PACKAGE = "org.xwalk.core";
75 private static boolean sAllowCrossPackage = false;
76 private static boolean sAlreadyUsingLibrary = false;
77 private static SharedXWalkExceptionHandler sExceptionHandler = null;
79 static void setExceptionHandler(SharedXWalkExceptionHandler handler) {
80 sExceptionHandler = handler;
83 static boolean isUsingLibrary() {
84 return sAlreadyUsingLibrary;
87 static boolean shouldUseLibrary() {
88 if (sAlreadyUsingLibrary) return true;
90 // TODO(wang16): There are many other conditions here.
91 // e.g. Whether application uses the ApplicationClass we provided,
92 // Whether native library arch is correct.
94 Class<?> delegateClass = null;
96 ClassLoader classLoader = ReflectionHelper.class.getClassLoader();
97 delegateClass = classLoader.loadClass(
98 INTERNAL_PACKAGE + "." + "XWalkViewDelegate");
99 } catch (ClassNotFoundException e) {
102 if (delegateClass == null) return true;
104 Method loadXWalkLibrary = delegateClass.getDeclaredMethod(
105 "loadXWalkLibrary", Context.class);
106 loadXWalkLibrary.invoke(null, (Context)null);
107 } catch (NoSuchMethodException e) {
109 } catch (IllegalArgumentException e) {
111 } catch (IllegalAccessException e) {
113 } catch (InvocationTargetException e) {
115 } catch (UnsatisfiedLinkError e) {
122 public static Context getBridgeContext() {
123 return sBridgeContext;
127 public static void allowCrossPackage() {
128 sAllowCrossPackage = true;
132 public static void init() {
135 if (shouldUseLibrary()) {
136 if (!sAllowCrossPackage) {
137 handleException("Use SharedXWalkView if you want to support shared mode");
139 XWalkApplication app = XWalkApplication.getApplication();
141 // TODO(wang16): Handle this well.
142 handleException("Shared mode requires XWalkApplication");
146 sBridgeContext = app.createPackageContext(
148 Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
149 sAlreadyUsingLibrary = true;
150 } catch (PackageManager.NameNotFoundException e) {
153 if (sBridgeContext != null) {
154 app.addResource(sBridgeContext.getResources());
155 initClassLoader(sBridgeContext.getClassLoader(), sBridgeContext);
158 initClassLoader(ReflectionHelper.class.getClassLoader(), null);
163 public static void initClassLoader(ClassLoader loader, Context bridgeContext) {
164 sBridgeOrWrapperLoader = loader;
165 sBridgeContext = bridgeContext;
166 sBridgeWrapperMap.clear();
167 sConstructorMap.clear();
169 for (String name : sConstructorHelperMap.keySet()) {
170 ConstructorHelper helper = sConstructorHelperMap.get(name);
171 if (helper != null) sConstructorMap.put(name, helper.loadConstructor());
174 // Load the helper in bridge side and invoke the initClassLoader method of it
175 // with wrapper's classloader via reflection.
176 Class<?> helperInBridge =
177 sBridgeOrWrapperLoader.loadClass(INTERNAL_PACKAGE + "." + "ReflectionHelper");
178 Method initInBridge = helperInBridge.getMethod(
179 "initClassLoader", ClassLoader.class, Context.class);
180 initInBridge.invoke(null, ReflectionHelper.class.getClassLoader(), sBridgeContext);
182 // JavascriptInterface is an annotation class bridge will use but declared in
184 Class<?> javascriptInterface =
185 sBridgeOrWrapperLoader.loadClass("org.xwalk.core.JavascriptInterface");
186 Class<?> xwalkContentInInternal =
187 ReflectionHelper.class.getClassLoader().loadClass(
188 INTERNAL_PACKAGE + "." + "XWalkContent");
189 Method setJavascriptInterface = xwalkContentInInternal.getDeclaredMethod(
190 "setJavascriptInterfaceClass", javascriptInterface.getClass());
191 setJavascriptInterface.invoke(null, javascriptInterface);
193 } catch (Exception e) {
198 public static void registerConstructor(String name, String clazz, Object... params) {
199 sConstructorHelperMap.put(name, new ConstructorHelper(clazz, params));
202 public static Class<?> loadClass(String clazz) {
203 // Any embedder using Embedding API should only use the exposed APIs which are
204 // in wrapper, so the initialization process is always starting from wrapper.
205 if (sBridgeOrWrapperLoader == null) init();
206 if (sBridgeOrWrapperLoader == null) return null;
208 return sBridgeOrWrapperLoader.loadClass(clazz);
209 } catch (ClassNotFoundException e) {
215 public static Method loadMethod(Class<?> clazz, String name, Object... paramTypes) {
216 if (sBridgeOrWrapperLoader == null) return null;
217 Class<?>[] params = new Class<?>[paramTypes.length];
218 for (int i = 0; i < paramTypes.length; i++) {
219 Object type = paramTypes[i];
220 if (type instanceof Class<?>) {
221 params[i] = (Class<?>) type;
222 } else if (type instanceof String) {
223 params[i] = loadClass((String) type);
227 return clazz.getMethod(name, params);
228 } catch (NoSuchMethodException e) {
234 public static void handleException(Exception e) {
237 if (isWrapper() && sExceptionHandler != null) {
238 if (sExceptionHandler.handleException(e)) return;
241 throw new RuntimeException(e);
244 public static void handleException(String e) {
245 handleException(new RuntimeException(e));
248 public static Object createInstance(String name, Object... parameters) {
250 Constructor<?> creator = sConstructorMap.get(name);
251 if (creator == null) {
252 ConstructorHelper helper = sConstructorHelperMap.get(name);
253 if (helper != null) {
254 creator = helper.loadConstructor();
255 sConstructorMap.put(name, creator);
258 if (creator != null) {
260 ret = creator.newInstance(parameters);
261 } catch (IllegalArgumentException e) {
263 } catch (InstantiationException e) {
265 } catch (IllegalAccessException e) {
267 } catch (InvocationTargetException e) {
274 public static Object invokeMethod(Method m, Object instance, Object... parameters) {
275 if (sBridgeOrWrapperLoader == null) return null;
279 ret = m.invoke(instance, parameters);
280 } catch (IllegalArgumentException e) {
282 } catch (IllegalAccessException e) {
284 } catch (InvocationTargetException e) {
286 } catch (NullPointerException e) {
293 // Convert between wrapper and bridge instance.
294 public static Object getBridgeOrWrapper(Object instance) {
295 if (sBridgeOrWrapperLoader == null) return null;
296 if (instance == null) return null;
297 Class<?> clazz = instance.getClass();
298 Method method = sBridgeWrapperMap.get(clazz);
299 if (method == null) {
300 String methodName = "getBridge";
302 methodName = "getWrapper";
305 method = clazz.getDeclaredMethod(methodName);
306 } catch (NoSuchMethodException e) {
310 if (method == null) {
311 return invokeMethod(method, instance);
313 sBridgeWrapperMap.put(clazz, method);
317 if (method.isAccessible()) return invokeMethod(method, instance);
319 // This is to enable the accessibility of getBridge temporarily.
320 // It's not public for documentation generating.
321 method.setAccessible(true);
322 Object ret = invokeMethod(method, instance);
323 method.setAccessible(false);
327 private static boolean isWrapper() {
328 return !ReflectionHelper.class.getPackage().getName().equals(INTERNAL_PACKAGE);
332 sIsWrapper = isWrapper();