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 }