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.support;
  17. import java.util.HashSet;
  18. import java.util.Iterator;
  19. import java.util.Set;
  20. import org.aopalliance.intercept.MethodInvocation;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. import org.springframework.aop.IntroductionInterceptor;
  24. /**
  25. * Convenient implementation of the IntroductionInterceptor interface.
  26. *
  27. * <p>Subclasses merely need to extend this class and implement the interfaces
  28. * to be introduced themselves. In this case the delegate is the subclass
  29. * instance itself. Alternatively a separate delegate may implement the
  30. * interface, and be set via the delegate bean property.
  31. *
  32. * <p>Delegates or subclasses may implement any number of interfaces.
  33. * All interfaces except IntroductionInterceptor are picked up from
  34. * the subclass or delegate by default.
  35. *
  36. * <p>The suppressInterface() method can be used to suppress interfaces
  37. * implemented by the delegate but which should not be introduced to the
  38. * owning AOP proxy.
  39. *
  40. * @author Rod Johnson
  41. * @version $Id: DelegatingIntroductionInterceptor.java,v 1.4 2004/04/21 10:26:21 jhoeller Exp $
  42. */
  43. public class DelegatingIntroductionInterceptor implements IntroductionInterceptor {
  44. protected final Log logger = LogFactory.getLog(getClass());
  45. /** Set of Class */
  46. private Set publishedInterfaces = new HashSet();
  47. /**
  48. * Object that actually implements the interfaces.
  49. * May be "this" if a subclass implements the introduced interfaces.
  50. */
  51. private Object delegate;
  52. /**
  53. * Construct a new DelegatingIntroductionInterceptor, providing
  54. * a delegate that implements the interfaces to be introduced.
  55. * @param delegate the delegate that implements the introduced interfaces
  56. */
  57. public DelegatingIntroductionInterceptor(Object delegate) {
  58. init(delegate);
  59. }
  60. /**
  61. * Construct a new DelegatingIntroductionInterceptor.
  62. * The delegate will be the subclass, which must implement
  63. * additional interfaces.
  64. */
  65. protected DelegatingIntroductionInterceptor() {
  66. init(this);
  67. }
  68. /**
  69. * Both constructors use this, as it's impossible to pass
  70. * "this" from one constructor to another.
  71. */
  72. private void init(Object delegate) {
  73. if (delegate == null) {
  74. throw new IllegalArgumentException("Delegate cannot be null in DelegatingIntroductionInterceptor");
  75. }
  76. this.delegate = delegate;
  77. this.publishedInterfaces.addAll(AopUtils.getAllInterfacesAsList(delegate));
  78. // We don't want to expose the control interface
  79. suppressInterface(IntroductionInterceptor.class);
  80. }
  81. /**
  82. * Suppress the specified interface, which will have
  83. * been autodetected due to its implementation by
  84. * the delegate.
  85. * Does nothing if it's not implemented by the delegate
  86. * @param intf interface to suppress
  87. */
  88. public void suppressInterface(Class intf) {
  89. this.publishedInterfaces.remove(intf);
  90. }
  91. public Class[] getIntroducedInterfaces() {
  92. return (Class[]) this.publishedInterfaces.toArray(new Class[this.publishedInterfaces.size()]);
  93. }
  94. public boolean implementsInterface(Class intf) {
  95. for (Iterator it = this.publishedInterfaces.iterator(); it.hasNext();) {
  96. Class pubIntf = (Class) it.next();
  97. if (intf.isAssignableFrom(pubIntf)) {
  98. return true;
  99. }
  100. }
  101. return false;
  102. }
  103. /**
  104. * Subclasses may need to override this if they want to perform custom
  105. * behaviour in around advice. However, subclasses should invoke this
  106. * method, which handles introduced interfaces and forwarding to the target.
  107. */
  108. public Object invoke(MethodInvocation mi) throws Throwable {
  109. if (isMethodOnIntroducedInterface(mi)) {
  110. if (logger.isDebugEnabled()) {
  111. logger.debug("Invoking self on invocation [" + mi + "]; breaking interceptor chain");
  112. }
  113. return mi.getMethod().invoke(this.delegate, mi.getArguments());
  114. }
  115. // If we get here, just pass the invocation on
  116. return mi.proceed();
  117. }
  118. /**
  119. * Is this method on an introduced interface?
  120. * @param mi method invocation
  121. * @return whether the method is on an introduced interface
  122. */
  123. protected final boolean isMethodOnIntroducedInterface(MethodInvocation mi) {
  124. return implementsInterface(mi.getMethod().getDeclaringClass());
  125. }
  126. }