001 // Copyright 2007 Regents of the University of California. May be used
002 // under the terms of the revised BSD license. See LICENSING for details.
003 package org.joe_e.reflect;
004
005 import java.lang.reflect.InvocationHandler;
006 import java.lang.reflect.Modifier;
007 import java.lang.reflect.Proxy;
008
009 import org.joe_e.Equatable;
010 import org.joe_e.Immutable;
011 import org.joe_e.JoeE;
012 import org.joe_e.Powerless;
013 import org.joe_e.Selfless;
014
015 /**
016 * The dynamic proxy interface. This is a wrapper around Java's dynamic proxy
017 * features provided by <code>java.lang.reflect.Proxy</code>.
018 */
019 public final class Proxies {
020
021 private Proxies() {}
022
023 /**
024 * Extract the <code>InvocationHandler</code> from a Proxy
025 * @param proxy {@link #proxy proxy} instance
026 * @return the invocation handler for the proxy
027 */
028 static public InvocationHandler getHandler(final Proxy proxy) {
029 return Proxy.getInvocationHandler(proxy);
030 }
031
032 /**
033 * The boot class loader.
034 */
035 static private final ClassLoader boot = Runnable.class.getClassLoader();
036
037 /**
038 * Constructs a dynamic proxy.
039 * @param handler invocation handler
040 * @param interfaces each implemented interface
041 * @return dynamic proxy
042 * @throws ClassCastException a restriction on the
043 * <code>types</code> is violated
044 * @throws NullPointerException an argument is <code>null</code>
045 */
046 static public Object proxy(final InvocationHandler handler,
047 final Class<?>... interfaces)
048 throws ClassCastException {
049 if (handler == null || interfaces == null) {
050 throw new NullPointerException();
051 }
052
053 // Determine the classloader.
054 ClassLoader proxyLoader = boot;
055
056 boolean equatable = false; // Was the Equatable type claimed?
057 boolean selfless = false; // Was the Selfless type claimed?
058 boolean immutable = false; // Was the Immutable type claimed?
059 boolean powerless = false; // Was the Powerless type claimed?
060
061 for (final Class<?> i : interfaces) {
062
063 // Can only implement public interfaces.
064 if (!Modifier.isPublic(i.getModifiers())) {
065 throw new ClassCastException();
066 }
067
068 // Perform Joe-E auditor checks.
069 if (!powerless && JoeE.isSubtypeOf(i, Powerless.class)) {
070 if (JoeE.instanceOf(handler, Powerless.class)) {
071 powerless = true;
072 immutable = true;
073 } else {
074 throw new ClassCastException();
075 }
076 }
077 if (!immutable && JoeE.isSubtypeOf(i, Immutable.class)) {
078 if (JoeE.instanceOf(handler, Immutable.class)) {
079 immutable = true;
080 } else {
081 throw new ClassCastException();
082 }
083 }
084 if (!equatable && JoeE.isSubtypeOf(i, Equatable.class)) {
085 // No additional checks are needed here, as we know that Proxy
086 // is not Selfless. An Equatable Proxy can have an identity-
087 // based equals() method if it so chooses by casting the Proxy
088 // object to Equatable.
089 if (selfless) {
090 throw new ClassCastException();
091 }
092 equatable = true;
093 }
094 if (!selfless && JoeE.isSubtypeOf(i, Selfless.class)) {
095 // No additional checks are needed here, because Object's
096 // equals() method is not available from the invocation handler
097 // and Proxy does not expose this method. Dynamic proxies are
098 // thus the only Selfless user class whose direct supertype is
099 // neither a Selfless class nor Object.
100 if (equatable) {
101 throw new ClassCastException();
102 }
103 selfless = true;
104 }
105
106 final ClassLoader interfaceLoader = i.getClassLoader();
107
108 // TODO: This will change if there is runtime info on banned
109 // interfaces or if a Library interface is added.
110 // if (interfaceLoader == boot && i != Runnable.class) {
111 // throw new ClassCastException();
112 //}
113
114 // Prefer any classloader over the bootstrap classloader.
115 if (proxyLoader == boot) {
116 proxyLoader = interfaceLoader;
117 }
118 }
119 try {
120 return Proxy.newProxyInstance(proxyLoader, interfaces, handler);
121 } catch (final IllegalArgumentException e) {
122 throw new ClassCastException(e.getMessage());
123 }
124 }
125
126 /**
127 * Returns <code>true</code> if the argument is an interface that can be
128 * implemented by a Proxy using <code>proxy()</code>.
129 * @param type candidate interface
130 * @return <code>true</code> if a Joe-E type, else <code>false</code>
131 */
132 static public boolean isImplementable(final Class<?> type) {
133 return type.isInterface() && Modifier.isPublic(type.getModifiers());
134
135 // return Runnable.class == type ||
136 // (type.isInterface() && type.getClassLoader() != boot &&
137 // Modifier.isPublic(type.getModifiers()));
138 // Can't consult taming database as it doesn't have proper info
139 // TODO: This will change if there are interfaces the implementation of
140 // which must be banned (e.g. Library)
141 }
142 }