001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.lang.reflect;
019:
020: import java.io.Serializable;
021: import java.lang.ref.WeakReference;
022: import java.util.HashMap;
023: import java.util.Map;
024: import java.util.WeakHashMap;
025:
026: import org.apache.harmony.luni.internal.reflect.ProxyClassFile;
027:
028: import org.apache.harmony.luni.util.Msg;
029:
030: /**
031: * This class provides methods to creating dynamic proxy classes and instances.
032: *
033: * @see InvocationHandler
034: *
035: * @since 1.3
036: */
037: public class Proxy implements Serializable {
038:
039: private static final long serialVersionUID = -2222568056686623797L;
040:
041: // maps class loaders to created classes by interface names
042: private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>> loaderCache = new WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>>();
043:
044: // to find previously created types
045: private static final Map<Class<?>, String> proxyCache = new WeakHashMap<Class<?>, String>();
046:
047: private static int NextClassNameIndex = 0;
048:
049: protected InvocationHandler h;
050:
051: private Proxy() {
052: }
053:
054: protected Proxy(InvocationHandler h) {
055: this .h = h;
056: }
057:
058: /**
059: * Return the dynamically build class for the given interfaces, build a new
060: * one when necessary. The order of the interfaces is important.
061: *
062: * The interfaces must be visible from the supplied class loader; no
063: * duplicates are permitted. All non-public interfaces must be defined in
064: * the same package.
065: *
066: * @param loader
067: * the class loader that will define the proxy class.
068: * @param interfaces
069: * an array of <code>Class</code> objects, each one identifying
070: * an interface that the new proxy must implement
071: * @return a proxy class that implements all of the interfaces referred to
072: * in the contents of <code>interfaces</code>.
073: * @exception IllegalArgumentException
074: * @exception NullPointerException
075: * if either <code>interfaces</code> or any of its elements
076: * are <code>null</code>.
077: */
078: public static Class<?> getProxyClass(ClassLoader loader,
079: Class<?>[] interfaces) throws IllegalArgumentException {
080: // check that interfaces are a valid array of visible interfaces
081: if (interfaces == null) {
082: throw new NullPointerException();
083: }
084: String commonPackageName = null;
085: for (int i = 0, length = interfaces.length; i < length; i++) {
086: Class<?> next = interfaces[i];
087: if (next == null) {
088: throw new NullPointerException();
089: }
090: String name = next.getName();
091: if (!next.isInterface()) {
092: throw new IllegalArgumentException(Msg.getString(
093: "K00ed", name)); //$NON-NLS-1$
094: }
095: if (loader != next.getClassLoader()) {
096: try {
097: if (next != Class.forName(name, false, loader)) {
098: throw new IllegalArgumentException(Msg
099: .getString("K00ee", name)); //$NON-NLS-1$
100: }
101: } catch (ClassNotFoundException ex) {
102: throw new IllegalArgumentException(Msg.getString(
103: "K00ee", //$NON-NLS-1$
104: name));
105: }
106: }
107: for (int j = i + 1; j < length; j++) {
108: if (next == interfaces[j]) {
109: throw new IllegalArgumentException(Msg.getString(
110: "K00ef", //$NON-NLS-1$
111: name));
112: }
113: }
114: if (!Modifier.isPublic(next.getModifiers())) {
115: int last = name.lastIndexOf('.');
116: String p = last == -1 ? "" : name.substring(0, last); //$NON-NLS-1$
117: if (commonPackageName == null) {
118: commonPackageName = p;
119: } else if (!commonPackageName.equals(p)) {
120: throw new IllegalArgumentException(Msg
121: .getString("K00f0")); //$NON-NLS-1$
122: }
123: }
124: }
125:
126: // search cache for matching proxy class using the class loader
127: synchronized (loaderCache) {
128: Map<String, WeakReference<Class<?>>> interfaceCache = loaderCache
129: .get(loader);
130: if (interfaceCache == null) {
131: loaderCache
132: .put(
133: loader,
134: (interfaceCache = new HashMap<String, WeakReference<Class<?>>>()));
135: }
136:
137: String interfaceKey = ""; //$NON-NLS-1$
138: if (interfaces.length == 1) {
139: interfaceKey = interfaces[0].getName();
140: } else {
141: StringBuilder names = new StringBuilder();
142: for (int i = 0, length = interfaces.length; i < length; i++) {
143: names.append(interfaces[i].getName());
144: names.append(' ');
145: }
146: interfaceKey = names.toString();
147: }
148:
149: Class<?> newClass;
150: WeakReference<Class<?>> ref = interfaceCache
151: .get(interfaceKey);
152: if (ref == null) {
153: String nextClassName = "$Proxy" + NextClassNameIndex++; //$NON-NLS-1$
154: if (commonPackageName != null) {
155: nextClassName = commonPackageName
156: + "." + nextClassName; //$NON-NLS-1$
157: }
158: byte[] classFileBytes = ProxyClassFile.generateBytes(
159: nextClassName, interfaces);
160: newClass = defineClassImpl(loader, nextClassName
161: .replace('.', '/'), classFileBytes);
162: // Need a weak reference to the class so it can
163: // be unloaded if the class loader is discarded
164: interfaceCache.put(interfaceKey,
165: new WeakReference<Class<?>>(newClass));
166: synchronized (proxyCache) {
167: // the value is unused
168: proxyCache.put(newClass, ""); //$NON-NLS-1$
169: }
170: } else {
171: newClass = ref.get();
172: assert newClass != null : "\ninterfaceKey=\""
173: + interfaceKey + "\"" + "\nloaderCache=\""
174: + loaderCache + "\"" + "\nintfCache=\""
175: + interfaceCache + "\"" + "\nproxyCache=\""
176: + proxyCache + "\"";
177: }
178: return newClass;
179: }
180: }
181:
182: /**
183: * Return an instance of the dynamically build class for the given
184: * interfaces that forwards methods to the specified invocation handler.
185: *
186: * The interfaces must be visible from the supplied class loader; no
187: * duplicates are permitted. All non-public interfaces must be defined in
188: * the same package.
189: *
190: * @param loader
191: * the class loader that will define the proxy class.
192: * @param interfaces
193: * the list of interfaces to implement.
194: * @param h
195: * the invocation handler for the forwarded methods.
196: * @return a new proxy object that delegates to the handler <code>h</code>
197: * @exception IllegalArgumentException
198: * @exception NullPointerException
199: * if the interfaces or any of its elements are null.
200: */
201: public static Object newProxyInstance(ClassLoader loader,
202: Class<?>[] interfaces, InvocationHandler h)
203: throws IllegalArgumentException {
204: if (h == null) {
205: throw new NullPointerException();
206: }
207: try {
208: return getProxyClass(loader, interfaces).getConstructor(
209: new Class<?>[] { InvocationHandler.class })
210: .newInstance(new Object[] { h });
211: } catch (NoSuchMethodException ex) {
212: throw (InternalError) (new InternalError(ex.toString())
213: .initCause(ex));
214: } catch (IllegalAccessException ex) {
215: throw (InternalError) (new InternalError(ex.toString())
216: .initCause(ex));
217: } catch (InstantiationException ex) {
218: throw (InternalError) (new InternalError(ex.toString())
219: .initCause(ex));
220: } catch (InvocationTargetException ex) {
221: Throwable target = ex.getTargetException();
222: throw (InternalError) (new InternalError(target.toString())
223: .initCause(target));
224: }
225: }
226:
227: /**
228: * Return whether the supplied class is a dynamically generated proxy class.
229: *
230: * @param cl
231: * the class.
232: * @return true if the class is a proxy class and false otherwise.
233: * @exception NullPointerException
234: * if the class is null.
235: */
236: public static boolean isProxyClass(Class<?> cl) {
237: if (cl == null) {
238: throw new NullPointerException();
239: }
240: synchronized (proxyCache) {
241: return proxyCache.containsKey(cl);
242: }
243: }
244:
245: /**
246: * Return the proxy instance's invocation handler.
247: *
248: * @param proxy
249: * the proxy instance.
250: * @return the proxy's invocation handler object
251: * @exception IllegalArgumentException
252: * if the supplied <code>proxy</code> is not a proxy
253: * object.
254: */
255: public static InvocationHandler getInvocationHandler(Object proxy)
256: throws IllegalArgumentException {
257:
258: if (isProxyClass(proxy.getClass())) {
259: return ((Proxy) proxy).h;
260: }
261:
262: throw new IllegalArgumentException(Msg.getString("K00f1")); //$NON-NLS-1$
263: }
264:
265: private static native Class<?> defineClassImpl(
266: ClassLoader classLoader, String className,
267: byte[] classFileBytes);
268:
269: }
|