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.ejb.access;
  17. import java.lang.reflect.InvocationTargetException;
  18. import java.lang.reflect.Method;
  19. import java.rmi.RemoteException;
  20. import java.util.Arrays;
  21. import javax.ejb.CreateException;
  22. import javax.ejb.EJBObject;
  23. import org.aopalliance.aop.AspectException;
  24. import org.aopalliance.intercept.MethodInvocation;
  25. import org.springframework.remoting.RemoteAccessException;
  26. /**
  27. * <p>Basic invoker for a remote Stateless Session Bean.
  28. * "Creates" a new EJB instance for each invocation.
  29. *
  30. * <p>See {@link org.springframework.jndi.AbstractJndiLocator} for info on
  31. * how to specify the JNDI location of the target EJB.
  32. *
  33. * <p>In a bean container, this class is normally best used as a singleton. However,
  34. * if that bean container pre-instantiates singletons (as do the XML ApplicationContext
  35. * variants) you may have a problem if the bean container is loaded before the EJB
  36. * container loads the target EJB. That is because the JNDI lookup will be performed in
  37. * the init method of this class and cached, but the EJB will not have been bound at the
  38. * target location yet. The solution is to not pre-instantiate this factory object, but
  39. * allow it to be created on first use. In the XML containers, this is controlled via
  40. * the "lazy-init" attribute.
  41. *
  42. * <p>This invoker is typically used with an RMI business interface, which serves
  43. * as super-interface of the EJB component interface. Alternatively, this invoker
  44. * can also proxy a remote SLSB with a matching non-RMI business interface, i.e. an
  45. * interface that mirrors the EJB business methods but does not declare RemoteExceptions.
  46. * In the latter case, RemoteExceptions thrown by the EJB stub will automatically get
  47. * converted to Spring's unchecked RemoteAccessException.
  48. *
  49. * @author Rod Johnson
  50. * @author Juergen Hoeller
  51. * @since 09-May-2003
  52. * @version $Id: SimpleRemoteSlsbInvokerInterceptor.java,v 1.11 2004/05/18 07:54:00 jhoeller Exp $
  53. * @see org.springframework.remoting.RemoteAccessException
  54. */
  55. public class SimpleRemoteSlsbInvokerInterceptor extends AbstractRemoteSlsbInvokerInterceptor {
  56. /**
  57. * Constructor for use as JavaBean.
  58. * Sets "resourceRef" to false by default.
  59. * @see #setResourceRef
  60. */
  61. public SimpleRemoteSlsbInvokerInterceptor() {
  62. setResourceRef(false);
  63. }
  64. /**
  65. * Convenient constructor for programmatic use.
  66. * @see org.springframework.jndi.AbstractJndiLocator#setJndiName
  67. * @see org.springframework.jndi.AbstractJndiLocator#setResourceRef
  68. */
  69. public SimpleRemoteSlsbInvokerInterceptor(String jndiName, boolean resourceRef) throws AspectException {
  70. setJndiName(jndiName);
  71. setResourceRef(resourceRef);
  72. try {
  73. afterPropertiesSet();
  74. }
  75. catch (Exception ex) {
  76. throw new AspectException("Failed to create EJB invoker interceptor", ex);
  77. }
  78. }
  79. /**
  80. * This is the last invoker in the chain.
  81. * "Creates" a new EJB instance for each invocation.
  82. * Can be overridden for custom invocation strategies.
  83. */
  84. public Object invoke(MethodInvocation invocation) throws Throwable {
  85. EJBObject ejb = null;
  86. try {
  87. ejb = newSessionBeanInstance();
  88. Method method = invocation.getMethod();
  89. if (method.getDeclaringClass().isInstance(ejb)) {
  90. // directly implemented
  91. return method.invoke(ejb, invocation.getArguments());
  92. }
  93. else {
  94. // not directly implemented
  95. Method proxyMethod = ejb.getClass().getMethod(method.getName(), method.getParameterTypes());
  96. return proxyMethod.invoke(ejb, invocation.getArguments());
  97. }
  98. }
  99. catch (InvocationTargetException ex) {
  100. Throwable targetException = ex.getTargetException();
  101. logger.info("Method of remote EJB [" + getJndiName() + "] threw exception", ex.getTargetException());
  102. if (targetException instanceof RemoteException &&
  103. !Arrays.asList(invocation.getMethod().getExceptionTypes()).contains(RemoteException.class)) {
  104. throw new RemoteAccessException("Could not invoke remote EJB [" + getJndiName() + "]", targetException);
  105. }
  106. else if (targetException instanceof CreateException) {
  107. if (!Arrays.asList(invocation.getMethod().getExceptionTypes()).contains(RemoteException.class)) {
  108. throw new RemoteAccessException("Could not create remote EJB [" + getJndiName() + "]", targetException);
  109. }
  110. else {
  111. throw new RemoteException("Could not create remote EJB [" + getJndiName() + "]", targetException);
  112. }
  113. }
  114. else {
  115. throw targetException;
  116. }
  117. }
  118. catch (Throwable ex) {
  119. throw new AspectException("Failed to invoke remote EJB [" + getJndiName() + "]", ex);
  120. }
  121. finally {
  122. if (ejb != null) {
  123. try {
  124. ejb.remove();
  125. }
  126. catch (Throwable ex) {
  127. logger.warn("Could not invoker 'remove' on Stateless Session Bean proxy", ex);
  128. }
  129. }
  130. }
  131. }
  132. }