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: */package org.apache.openejb.assembler.classic;
017:
018: import java.util.List;
019: import java.util.ArrayList;
020: import java.lang.reflect.Method;
021:
022: /**
023: * @version $Rev: 602704 $ $Date: 2007-12-09 09:58:22 -0800 $
024: */
025: public class ProxyInterfaceResolver {
026:
027: public static List<Class> getInterfaces(Class implementation,
028: Class mainInterface, List<Class> interfaces) {
029: List<Class> valid = new ArrayList<Class>();
030: // The intended interface is safe to add
031: valid.add(mainInterface);
032:
033: // Any interface the bean implements is safe (potentially)
034: for (Class interfce : interfaces) {
035: if (interfce.isAssignableFrom(implementation)) {
036: valid.add(interfce);
037: }
038: }
039:
040: // Here comes the trick, if any of them implement java.rmi.Remote
041: // we have to check the "remote" methods against the other methods
042: // of the same name and params to see if there are any conflicts in
043: // the exception clauses. If there are, then we need to remove the
044: // conflicting interface(s).
045: //
046: // DETAILS:
047: // The trick is that two nearly matching interface methods
048: // -InterfaceA: void doIt() throws Foo;
049: // -InterfaceB: void doIt() throws Bar;
050: //
051: // can be implemented in a class by leaving out exceptions from the
052: // throws clause that aren't declared in both interfaces methods.
053: // -Implementation: void doIt(){}
054: //
055: // This means the implementing class can not throw Foo or Bar. The
056: // same rule applies to proxies created from these two interfaces as
057: // the proxy generating code will automatically remove exceptions
058: // not shared by the two matching interface methods; eliminating
059: // the proxy's and therefore container's ability to throw those
060: // exceptions.
061: //
062: // The specific issue with java.rmi.Remote interfaces is that per
063: // spec rules many runtime exceptions (container or connection issues)
064: // are thrown to clients as java.rmi.RemoteException which is not
065: // a runtime exception and must be throwable via the proxy.
066:
067: List<Class> remotes = new ArrayList<Class>();
068: List<Class> nonremotes = new ArrayList<Class>();
069: for (Class interfce : valid) {
070: if (java.rmi.Remote.class.isAssignableFrom(interfce)) {
071: remotes.add(interfce);
072: } else {
073: nonremotes.add(interfce);
074: }
075: }
076:
077: // No remote interfaces, we're good to go
078: if (remotes.size() == 0)
079: return valid;
080:
081: // -----------------------------------------------------------
082: // If we got here, we have potentially clashing interfaces
083: // We sort of have to start over and go "slower" checking
084: // methods for conflicts and not including those interfaces
085: // -----------------------------------------------------------
086:
087: valid.clear();
088: remotes.remove(mainInterface);
089: nonremotes.remove(mainInterface);
090:
091: // Re-add the main interface
092: valid.add(mainInterface);
093:
094: // Track the method signatures of the interfaces we add
095: List<Signature> proxySignatures = getSignatures(mainInterface);
096:
097: // Show affinity for the remote interfaces if the main
098: // interface is a java.rmi.Remote
099: if (java.rmi.Remote.class.isAssignableFrom(mainInterface)) {
100: for (Class interfce : remotes) {
101: addIfNotConflicting(interfce, valid, proxySignatures);
102: }
103: for (Class interfce : nonremotes) {
104: addIfNotConflicting(interfce, valid, proxySignatures);
105: }
106: } else {
107: for (Class interfce : nonremotes) {
108: addIfNotConflicting(interfce, valid, proxySignatures);
109: }
110: for (Class interfce : remotes) {
111: addIfNotConflicting(interfce, valid, proxySignatures);
112: }
113: }
114:
115: return valid;
116: }
117:
118: /**
119: * Adds the interface to the list of valid interfaces for the proxy
120: * if the signatures on the interface do not conflict with the method
121: * signatures already apart of the proxy's other interfaces.
122: *
123: * @param interfce
124: * @param valid
125: * @param proxySignatures
126: */
127: private static void addIfNotConflicting(Class interfce,
128: List<Class> valid, List<Signature> proxySignatures) {
129:
130: List<Signature> interfaceSigs = getSignatures(interfce);
131:
132: for (Signature sig : interfaceSigs) {
133: // Contains will return true if the
134: // method signature exits *and* has
135: // a different throws clause
136: if (proxySignatures.contains(sig)) {
137: return; // conflicts and cannot be added
138: }
139: }
140:
141: // Does not conflict, add it and track the new signatures
142: valid.add(interfce);
143: proxySignatures.addAll(interfaceSigs);
144: }
145:
146: private static List<Signature> getSignatures(Class mainInterface) {
147: List<Signature> sigs = new ArrayList<Signature>();
148: for (Method method : mainInterface.getMethods()) {
149: sigs.add(new Signature(mainInterface, method));
150: }
151: return sigs;
152: }
153:
154: public static class Signature {
155: private final Class clazz;
156: private final Method method;
157: private final String sig;
158:
159: public Signature(Class clazz, Method method) {
160: this .clazz = clazz;
161: this .method = method;
162: StringBuilder sb = new StringBuilder();
163: sb.append(method.getName());
164: sb.append('(');
165: for (Class<?> type : method.getParameterTypes()) {
166: sb.append(type.getName());
167: sb.append(',');
168: }
169: sb.append(')');
170: sig = sb.toString();
171: }
172:
173: public Method getMethod() {
174: return method;
175: }
176:
177: // This equals returns true only if the method signatures
178: // are the same *and* one is remote and one is not
179: public boolean equals(Object o) {
180: if (this == o)
181: return true;
182: if (o == null || getClass() != o.getClass())
183: return false;
184:
185: final Signature signature = (Signature) o;
186:
187: if (!sig.equals(signature.sig))
188: return false;
189:
190: boolean aIsRemote = java.rmi.Remote.class
191: .isAssignableFrom(clazz);
192: boolean bIsRemote = java.rmi.Remote.class
193: .isAssignableFrom(signature.clazz);
194:
195: return !(aIsRemote == bIsRemote);
196: }
197:
198: public int hashCode() {
199: return sig.hashCode();
200: }
201:
202: public String toString() {
203: return method.toString();
204: }
205: }
206:
207: }
|