1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.aop.framework;
  17. import java.util.HashSet;
  18. import java.util.Iterator;
  19. import java.util.LinkedList;
  20. import java.util.List;
  21. import java.util.Set;
  22. import org.aopalliance.intercept.Interceptor;
  23. import org.aopalliance.intercept.MethodInterceptor;
  24. import org.springframework.aop.Advisor;
  25. import org.springframework.aop.AfterReturningAdvice;
  26. import org.springframework.aop.IntroductionAdvisor;
  27. import org.springframework.aop.IntroductionInterceptor;
  28. import org.springframework.aop.MethodBeforeAdvice;
  29. import org.springframework.aop.Pointcut;
  30. import org.springframework.aop.TargetSource;
  31. import org.springframework.aop.ThrowsAdvice;
  32. import org.springframework.aop.support.DefaultPointcutAdvisor;
  33. import org.springframework.aop.target.SingletonTargetSource;
  34. import org.springframework.util.StringUtils;
  35. /**
  36. * Superclass for AOP Proxy configuration managers.
  37. * These are not themselves AOP proxies, but
  38. * subclasses of this class are normally factories from which
  39. * AOP proxy instances are obtained directly.
  40. *
  41. * <p>This class frees subclasses of the housekeeping of Interceptors
  42. * and Advisors, but doesn't actually implement proxy creation
  43. * methods, which are provided by subclasses.
  44. *
  45. * @author Rod Johnson
  46. * @version $Id: AdvisedSupport.java,v 1.30 2004/05/27 08:42:15 jhoeller Exp $
  47. * @see org.springframework.aop.framework.AopProxy
  48. */
  49. public class AdvisedSupport extends ProxyConfig implements Advised {
  50. /**
  51. * Canonical TargetSource when there's no target, and behavior is supplied
  52. * by the advisors.
  53. */
  54. public static final TargetSource EMPTY_TARGET_SOURCE = new TargetSource() {
  55. public Class getTargetClass() {
  56. return null;
  57. }
  58. public boolean isStatic() {
  59. return true;
  60. }
  61. public Object getTarget() {
  62. return null;
  63. }
  64. public void releaseTarget(Object target) {
  65. }
  66. };
  67. /** List of AdvisedSupportListener */
  68. private final List listeners = new LinkedList();
  69. TargetSource targetSource = EMPTY_TARGET_SOURCE;
  70. AdvisorChainFactory advisorChainFactory;
  71. /**
  72. * List of Advice. If an Interceptor is added, it will be wrapped
  73. * in an Advice before being added to this List.
  74. */
  75. private List advisors = new LinkedList();
  76. /**
  77. * Array updated on changes to the advisors list,
  78. * which is easier to manipulate internally
  79. */
  80. private Advisor[] advisorsArray = new Advisor[0];
  81. /** Interfaces to be implemented by the proxy */
  82. private Set interfaces = new HashSet();
  83. /**
  84. * Set to true when the first AOP proxy has been created, meaning that we must
  85. * track advice changes via onAdviceChange() callback.
  86. */
  87. private boolean isActive;
  88. /**
  89. * No arg constructor to allow use as a JavaBean.
  90. */
  91. public AdvisedSupport() {
  92. setAdvisorChainFactory(new HashMapCachingAdvisorChainFactory());
  93. }
  94. /**
  95. * Create a DefaultProxyConfig with the given parameters.
  96. * @param interfaces the proxied interfaces
  97. */
  98. public AdvisedSupport(Class[] interfaces) {
  99. this();
  100. setInterfaces(interfaces);
  101. }
  102. public void addListener(AdvisedSupportListener listener) {
  103. this.listeners.add(listener);
  104. }
  105. public void removeListener(AdvisedSupportListener listener) {
  106. this.listeners.remove(listener);
  107. }
  108. public void setTarget(Object target) {
  109. setTargetSource(new SingletonTargetSource(target));
  110. }
  111. public void setTargetSource(TargetSource targetSource) {
  112. if (isActive() && getOptimize()) {
  113. throw new AopConfigException("Can't change target with an optimized CGLIB proxy: it has it's own target");
  114. }
  115. this.targetSource = targetSource;
  116. }
  117. public TargetSource getTargetSource() {
  118. return this.targetSource;
  119. }
  120. public void setAdvisorChainFactory(AdvisorChainFactory advisorChainFactory) {
  121. this.advisorChainFactory = advisorChainFactory;
  122. addListener(advisorChainFactory);
  123. }
  124. public AdvisorChainFactory getAdvisorChainFactory() {
  125. return this.advisorChainFactory;
  126. }
  127. /**
  128. * Call this method on a new instance created by the no-arg constructor
  129. * to create an independent copy of the configuration from the other.
  130. * <p>Does not copy MethodInvocationFactory; a parameter should be provided
  131. * to the constructor if necessary. Note that the same MethodInvocationFactory
  132. * should <b>not</b> be used for the new instance, or it may not be independent.
  133. * @param other DefaultProxyConfig to copy configuration from
  134. */
  135. protected void copyConfigurationFrom(AdvisedSupport other) {
  136. copyFrom(other);
  137. this.targetSource = other.targetSource;
  138. setInterfaces((Class[]) other.interfaces.toArray(new Class[other.interfaces.size()]));
  139. this.advisors = new LinkedList();
  140. for (int i = 0; i < other.advisors.size(); i++) {
  141. Advisor advice = (Advisor) other.advisors.get(i);
  142. addAdvisor(advice);
  143. }
  144. }
  145. /**
  146. * Set the interfaces to be proxied.
  147. */
  148. public void setInterfaces(Class[] interfaces) {
  149. this.interfaces.clear();
  150. for (int i = 0; i < interfaces.length; i++) {
  151. addInterface(interfaces[i]);
  152. }
  153. }
  154. /**
  155. * Add a new proxied interface.
  156. * @param newInterface additional interface to proxy
  157. */
  158. public void addInterface(Class newInterface) {
  159. this.interfaces.add(newInterface);
  160. adviceChanged();
  161. logger.debug("Added new aspect interface: " + newInterface);
  162. }
  163. public Class[] getProxiedInterfaces() {
  164. return (Class[]) this.interfaces.toArray(new Class[this.interfaces.size()]);
  165. }
  166. /**
  167. * Remove a proxied interface.
  168. * Does nothing if it isn't proxied.
  169. */
  170. public boolean removeInterface(Class intf) {
  171. return this.interfaces.remove(intf);
  172. }
  173. public void addInterceptor(Interceptor interceptor) throws AopConfigException {
  174. int pos = (this.advisors != null) ? this.advisors.size() : 0;
  175. addInterceptor(pos, interceptor);
  176. }
  177. public boolean isInterfaceProxied(Class intf) {
  178. for (Iterator it = this.interfaces.iterator(); it.hasNext();) {
  179. Class proxyIntf = (Class) it.next();
  180. if (intf.isAssignableFrom(proxyIntf)) {
  181. return true;
  182. }
  183. }
  184. return false;
  185. }
  186. /**
  187. * Cannot add IntroductionInterceptors this way.
  188. */
  189. public void addInterceptor(int pos, Interceptor interceptor) throws AopConfigException {
  190. if (!(interceptor instanceof MethodInterceptor)) {
  191. throw new AopConfigException(getClass().getName() + " only handles MethodInterceptors");
  192. }
  193. if (interceptor instanceof IntroductionInterceptor) {
  194. throw new AopConfigException("IntroductionInterceptors may only be added as part of IntroductionAdvice");
  195. }
  196. addAdvisor(pos, new DefaultPointcutAdvisor(interceptor));
  197. }
  198. /**
  199. * Convenience method to remove an interceptor.
  200. */
  201. public final boolean removeInterceptor(Interceptor interceptor) throws AopConfigException {
  202. int index = indexOf(interceptor);
  203. if (index == -1) {
  204. return false;
  205. }
  206. else {
  207. removeAdvisor(index);
  208. return true;
  209. }
  210. }
  211. public void addAfterReturningAdvice(final AfterReturningAdvice ara) throws AopConfigException {
  212. addAdvisor(new DefaultPointcutAdvisor(Pointcut.TRUE, ara));
  213. }
  214. public void addBeforeAdvice(final MethodBeforeAdvice ba) throws AopConfigException {
  215. addAdvisor(new DefaultPointcutAdvisor(Pointcut.TRUE, ba));
  216. }
  217. public void addThrowsAdvice(final ThrowsAdvice throwsAdvice) throws AopConfigException {
  218. addAdvisor(new DefaultPointcutAdvisor(throwsAdvice));
  219. }
  220. /**
  221. * Return the index (from 0) of the given AOP Alliance interceptor,
  222. * or -1 if no such interceptor is an advice for this proxy.
  223. * The return value of this method can be used to index into
  224. * the Advisors array.
  225. * @param interceptor AOP Alliance interceptor to search for
  226. * @return index from 0 of this interceptor, or -1 if there's
  227. * no such advice.
  228. */
  229. public int indexOf(Interceptor interceptor) {
  230. for (int i = 0; i < this.advisors.size(); i++) {
  231. Advisor advisor = (Advisor) this.advisors.get(i);
  232. if (advisor.getAdvice() == interceptor) {
  233. return i;
  234. }
  235. }
  236. return -1;
  237. }
  238. /**
  239. * Return the index (from 0) of the given advisor,
  240. * or -1 if no such advisor applies to this proxy.
  241. * The return value of this method can be used to index into
  242. * the Advisors array.
  243. * @param advisor advisor to search for
  244. * @return index from 0 of this advisor, or -1 if there's
  245. * no such advisor.
  246. */
  247. public int indexOf(Advisor advisor) {
  248. return this.advisors.indexOf(advisor);
  249. }
  250. public final boolean removeAdvisor(Advisor advisor) {
  251. int index = indexOf(advisor);
  252. if (index == -1) {
  253. return false;
  254. }
  255. else {
  256. removeAdvisor(index);
  257. return true;
  258. }
  259. }
  260. public void removeAdvisor(int index) throws AopConfigException {
  261. if (isFrozen())
  262. throw new AopConfigException("Cannot remove Advisor: config is frozen");
  263. if (index < 0 || index > advisors.size() - 1)
  264. throw new AopConfigException("Advisor index " + index + " is out of bounds: " +
  265. "Only have " + advisors.size() + " advisors");
  266. Advisor advisor = (Advisor) advisors.get(index);
  267. if (advisor instanceof IntroductionAdvisor) {
  268. IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
  269. // We need to remove interfaces
  270. for (int j = 0; j < ia.getInterfaces().length; j++) {
  271. removeInterface(ia.getInterfaces()[j]);
  272. }
  273. }
  274. this.advisors.remove(index);
  275. updateAdvisorsArray();
  276. adviceChanged();
  277. }
  278. private void addAdvisorInternal(int pos, Advisor advice) throws AopConfigException {
  279. if (isFrozen()) {
  280. throw new AopConfigException("Cannot add advisor: config is frozen");
  281. }
  282. this.advisors.add(pos, advice);
  283. updateAdvisorsArray();
  284. adviceChanged();
  285. }
  286. public void addAdvisor(int pos, IntroductionAdvisor advisor) throws AopConfigException {
  287. advisor.validateInterfaces();
  288. // if the advisor passed validation we can make the change
  289. for (int i = 0; i < advisor.getInterfaces().length; i++) {
  290. addInterface(advisor.getInterfaces()[i]);
  291. }
  292. addAdvisorInternal(pos, advisor);
  293. }
  294. public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
  295. if (advisor instanceof IntroductionAdvisor) {
  296. addAdvisor(pos, (IntroductionAdvisor) advisor);
  297. }
  298. else {
  299. addAdvisorInternal(pos, advisor);
  300. }
  301. }
  302. public void addAdvisor(Advisor advice) {
  303. int pos = this.advisors.size();
  304. addAdvisor(pos, advice);
  305. }
  306. /**
  307. * Bring the array up to date with the list.
  308. */
  309. private void updateAdvisorsArray() {
  310. this.advisorsArray = (Advisor[]) this.advisors.toArray(new Advisor[this.advisors.size()]);
  311. }
  312. public final Advisor[] getAdvisors() {
  313. return this.advisorsArray;
  314. }
  315. /**
  316. * Replace the given advisor.
  317. * <b>NB:</b>If the advisor is an IntroductionAdvisor
  318. * and the replacement is not or implements different interfaces,
  319. * the proxy will need to be re-obtained or the old interfaces
  320. * won't be supported and the new interface won't be implemented.
  321. * @param a advisor to replace
  322. * @param b advisor to replace it with
  323. * @return whether it was replaced. If the advisor wasn't found in the
  324. * list of advisors, this method returns false and does nothing.
  325. */
  326. public final boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException {
  327. int index = indexOf(a);
  328. if (index == -1 || b == null) {
  329. return false;
  330. }
  331. removeAdvisor(index);
  332. addAdvisor(index, b);
  333. return true;
  334. }
  335. /**
  336. * Is this interceptor included in any advisor?
  337. * @param mi interceptor to check inclusion of
  338. * @return whether this interceptor instance could be run in an invocation
  339. */
  340. public final boolean interceptorIncluded(Interceptor mi) {
  341. if (this.advisors.size() == 0) {
  342. return false;
  343. }
  344. for (int i = 0; i < this.advisors.size(); i++) {
  345. Advisor advice = (Advisor) this.advisors.get(i);
  346. if (advice.getAdvice() == mi) {
  347. return true;
  348. }
  349. }
  350. return false;
  351. }
  352. /**
  353. * Count interceptors of the given class
  354. * @param interceptorClass class of the interceptor to check
  355. * @return the count of the interceptors of this class or subclasses
  356. */
  357. public final int countInterceptorsOfType(Class interceptorClass) {
  358. if (this.advisors.size() == 0) {
  359. return 0;
  360. }
  361. int count = 0;
  362. for (int i = 0; i < this.advisors.size(); i++) {
  363. Advisor advisor = (Advisor) this.advisors.get(i);
  364. if (interceptorClass.isAssignableFrom(advisor.getAdvice().getClass())) {
  365. ++count;
  366. }
  367. }
  368. return count;
  369. }
  370. /**
  371. * Invoked when advice has changed.
  372. */
  373. private synchronized void adviceChanged() {
  374. if (this.isActive) {
  375. for (int i = 0; i < this.listeners.size(); i++) {
  376. ((AdvisedSupportListener) this.listeners.get(i)).adviceChanged(this);
  377. }
  378. }
  379. }
  380. private void activate() {
  381. this.isActive = true;
  382. for (int i = 0; i < this.listeners.size(); i++) {
  383. ((AdvisedSupportListener) this.listeners.get(i)).activated(this);
  384. }
  385. }
  386. /**
  387. * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
  388. * create an AOP proxy with this as an argument.
  389. */
  390. protected synchronized AopProxy createAopProxy() {
  391. if (!this.isActive) {
  392. activate();
  393. }
  394. return getAopProxyFactory().createAopProxy(this);
  395. }
  396. /**
  397. * Subclasses can call this to check whether any AOP proxies have been created yet.
  398. */
  399. protected final boolean isActive() {
  400. return isActive;
  401. }
  402. public String toProxyConfigString() {
  403. return toString();
  404. }
  405. /**
  406. * For debugging/diagnostic use.
  407. */
  408. public String toString() {
  409. StringBuffer sb = new StringBuffer(getClass().getName() + ": ");
  410. sb.append(this.interfaces.size()).append(" interfaces=[");
  411. sb.append(StringUtils.collectionToCommaDelimitedString(this.interfaces)).append("]; ");
  412. sb.append(this.advisors.size()).append(" pointcuts=[");
  413. sb.append(StringUtils.collectionToCommaDelimitedString(this.advisors)).append("]; ");
  414. sb.append("targetSource=[").append(this.targetSource).append("]; ");
  415. sb.append("advisorChainFactory=").append(this.advisorChainFactory);
  416. sb.append(super.toString());
  417. return sb.toString();
  418. }
  419. }