/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2009-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.weld.services;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.enterprise.inject.spi.InterceptionType;
import javax.enterprise.inject.spi.Interceptor;
import javax.interceptor.AroundInvoke;
import javax.interceptor.AroundTimeout;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.glassfish.ejb.api.EjbContainerServices;
import org.glassfish.weld.ejb.EjbDescriptorImpl;
import org.glassfish.weld.ejb.SessionObjectReferenceImpl;
import org.jboss.weld.ejb.api.SessionObjectReference;
import org.jboss.weld.ejb.spi.EjbDescriptor;
import org.jboss.weld.ejb.spi.EjbServices;
import org.jboss.weld.ejb.spi.InterceptorBindings;
import org.jvnet.hk2.component.Habitat;
import com.sun.enterprise.deployment.EjbBundleDescriptor;
import com.sun.enterprise.deployment.EjbInterceptor;
import com.sun.enterprise.deployment.EjbSessionDescriptor;
import com.sun.enterprise.deployment.LifecycleCallbackDescriptor;
/**
* An implementation of th EJBServices
Weld SPI. The Weld
* implementation uses this SPI to resolve EJB and register CDI Interceptors
* for EJBs.
*/
public class EjbServicesImpl implements EjbServices
{
private Habitat habitat;
private Logger logger = Logger.getLogger(EjbServicesImpl.class.getName());
public EjbServicesImpl(Habitat h) {
habitat = h;
}
/**
* Request a reference to an EJB session object from the container. If the
* EJB being resolved is a stateful session bean, the container should ensure
* the session bean is created before this method returns.
*
* @param ejbDescriptor the ejb to resolve
* @return a reference to the session object
*/
public SessionObjectReference resolveEjb(EjbDescriptor> ejbDescriptor) {
SessionObjectReference sessionObj = null;
// All we need to do is create a reference based on one of the beans'
// client views, so just choose one and get its corresponding portable
// JNDI name.
String globalJndiName = getDefaultGlobalJndiName(ejbDescriptor);
if( globalJndiName != null ) {
try {
InitialContext ic = new InitialContext();
Object ejbRef = ic.lookup(globalJndiName);
EjbContainerServices containerServices = habitat.getByContract(EjbContainerServices.class);
sessionObj = new SessionObjectReferenceImpl(containerServices, ejbRef);
} catch(NamingException ne) {
throw new IllegalStateException("Error resolving session object reference for ejb name " +
ejbDescriptor.getBeanClass() + " and jndi name " + globalJndiName, ne);
}
} else {
throw new IllegalArgumentException("Not enough type information to resolve ejb for " +
" ejb name " + ejbDescriptor.getBeanClass());
}
return sessionObj;
}
private String getDefaultGlobalJndiName(EjbDescriptor ejbDesc) {
EjbSessionDescriptor sessionDesc = (EjbSessionDescriptor)
((EjbDescriptorImpl) ejbDesc).getEjbDescriptor();
String clientView = null;
if( sessionDesc.isLocalBean() ) {
clientView = sessionDesc.getEjbClassName();
} else if( sessionDesc.getLocalBusinessClassNames().size() >= 1) {
clientView = sessionDesc.getLocalBusinessClassNames().iterator().next();
} else if( sessionDesc.getRemoteBusinessClassNames().size() >= 1) {
clientView = sessionDesc.getRemoteBusinessClassNames().iterator().next();
}
return (clientView != null) ? sessionDesc.getPortableJndiName(clientView) : null;
}
public void registerInterceptors(EjbDescriptor> ejbDesc, InterceptorBindings interceptorBindings) {
// Work around bug that ejbDesc might be internal 299 descriptor.
if( ejbDesc instanceof org.jboss.weld.ejb.InternalEjbDescriptor ) {
ejbDesc = ((org.jboss.weld.ejb.InternalEjbDescriptor)ejbDesc).delegate();
}
com.sun.enterprise.deployment.EjbDescriptor glassfishEjbDesc = (com.sun.enterprise.deployment.EjbDescriptor)
((EjbDescriptorImpl) ejbDesc).getEjbDescriptor();
// Convert to EjbInterceptor
// First create master list of EjbInterceptor descriptors
for(Interceptor next : interceptorBindings.getAllInterceptors()) {
logger.log(Level.FINE, "trying to register:" + next);
// Add interceptor to list all interceptors in ejb descriptor
if( !(glassfishEjbDesc.hasInterceptorClass(next.getBeanClass().getName()))) {
logger.log(Level.FINE, "Adding interceptor: "
+ next.getBeanClass().getName() + " for EJB:"
+ glassfishEjbDesc.getEjbClassName());
EjbInterceptor ejbInt = makeEjbInterceptor(next, glassfishEjbDesc.getEjbBundleDescriptor());
glassfishEjbDesc.addInterceptorClass(ejbInt);
}
}
// Create ordered list of EjbInterceptor for each lifecycle interception type and append to
// EjbDescriptor. 299 interceptors are always added after any interceptors defined via
// EJB-defined metadata, so the ordering will be correct since all the ejb interceptors
// have already been processed.
List postConstructChain =
makeInterceptorChain(InterceptionType.POST_CONSTRUCT,
interceptorBindings.getLifecycleInterceptors(InterceptionType.POST_CONSTRUCT),
glassfishEjbDesc);
glassfishEjbDesc.appendToInterceptorChain(postConstructChain);
List preDestroyChain =
makeInterceptorChain(InterceptionType.PRE_DESTROY,
interceptorBindings.getLifecycleInterceptors(InterceptionType.PRE_DESTROY),
glassfishEjbDesc);
glassfishEjbDesc.appendToInterceptorChain(preDestroyChain);
List prePassivateChain =
makeInterceptorChain(InterceptionType.PRE_PASSIVATE,
interceptorBindings.getLifecycleInterceptors(InterceptionType.PRE_PASSIVATE),
glassfishEjbDesc);
glassfishEjbDesc.appendToInterceptorChain(prePassivateChain);
List postActivateChain =
makeInterceptorChain(InterceptionType.POST_ACTIVATE,
interceptorBindings.getLifecycleInterceptors(InterceptionType.POST_ACTIVATE),
glassfishEjbDesc);
glassfishEjbDesc.appendToInterceptorChain(postActivateChain);
// 299-provided list is organized as per-method. Append each method chain to EjbDescriptor.
Class ejbBeanClass = null;
try {
ClassLoader cl = glassfishEjbDesc.getEjbBundleDescriptor().getClassLoader();
ejbBeanClass = cl.loadClass(glassfishEjbDesc.getEjbClassName());
} catch(ClassNotFoundException cnfe) {
throw new IllegalStateException("Cannot load bean class " + glassfishEjbDesc.getEjbClassName(),
cnfe);
}
for(Method m : ejbBeanClass.getMethods()) {
List aroundInvokeChain =
makeInterceptorChain(InterceptionType.AROUND_INVOKE,
interceptorBindings.getMethodInterceptors(InterceptionType.AROUND_INVOKE, m),
glassfishEjbDesc);
glassfishEjbDesc.addMethodLevelChain(aroundInvokeChain, m, true);
List aroundTimeoutChain =
makeInterceptorChain(InterceptionType.AROUND_TIMEOUT,
interceptorBindings.getMethodInterceptors(InterceptionType.AROUND_TIMEOUT, m),
glassfishEjbDesc);
glassfishEjbDesc.addMethodLevelChain(aroundTimeoutChain, m, false);
}
return;
}
private List makeInterceptorChain(InterceptionType interceptionType,
List> lifecycleList,
com.sun.enterprise.deployment.EjbDescriptor ejbDesc ) {
List ejbInterceptorList = new LinkedList();
if( lifecycleList == null ) {
return ejbInterceptorList;
}
for(Interceptor next : lifecycleList ) {
EjbInterceptor ejbInt = makeEjbInterceptor(next, ejbDesc.getEjbBundleDescriptor());
LifecycleCallbackDescriptor lifecycleDesc = new LifecycleCallbackDescriptor();
lifecycleDesc.setLifecycleCallbackClass(next.getBeanClass().getName());
lifecycleDesc.setLifecycleCallbackMethod(getInterceptorMethod(next.getBeanClass(),
getInterceptorAnnotationType(interceptionType)));
switch(interceptionType) {
case POST_CONSTRUCT :
ejbInt.addPostConstructDescriptor(lifecycleDesc);
break;
case PRE_DESTROY :
ejbInt.addPreDestroyDescriptor(lifecycleDesc);
break;
case PRE_PASSIVATE :
ejbInt.addPrePassivateDescriptor(lifecycleDesc);
break;
case POST_ACTIVATE :
ejbInt.addPostActivateDescriptor(lifecycleDesc);
break;
case AROUND_INVOKE :
ejbInt.addAroundInvokeDescriptor(lifecycleDesc);
break;
case AROUND_TIMEOUT :
ejbInt.addAroundTimeoutDescriptor(lifecycleDesc);
break;
default :
throw new IllegalArgumentException("Invalid lifecycle interception type " +
interceptionType);
}
ejbInterceptorList.add(ejbInt);
}
return ejbInterceptorList;
}
private Class getInterceptorAnnotationType(InterceptionType interceptionType) {
switch(interceptionType) {
case POST_CONSTRUCT :
return PostConstruct.class;
case PRE_DESTROY :
return PreDestroy.class;
case PRE_PASSIVATE :
return PrePassivate.class;
case POST_ACTIVATE :
return PostActivate.class;
case AROUND_INVOKE :
return AroundInvoke.class;
case AROUND_TIMEOUT :
return AroundTimeout.class;
}
throw new IllegalArgumentException("Invalid interception type " +
interceptionType);
}
private String getInterceptorMethod(Class interceptorClass, Class annotation) {
for(Method next : interceptorClass.getDeclaredMethods()) {
if( next.getAnnotation(annotation) != null ) {
return next.getName();
}
}
throw new IllegalStateException("Interceptor Class " + interceptorClass + " has no method annotated with " +
annotation);
}
private EjbInterceptor makeEjbInterceptor(Interceptor interceptor, EjbBundleDescriptor bundle) {
EjbInterceptor ejbInt = new EjbInterceptor();
ejbInt.setBundleDescriptor(bundle);
ejbInt.setInterceptorClassName(interceptor.getBeanClass().getName());
ejbInt.setCDIInterceptor(true);
return ejbInt;
}
private EjbInterceptor getEjbInterceptorByClassName(Set allInterceptors, String name) {
for(EjbInterceptor next : allInterceptors) {
if( next.getInterceptorClassName().equals(name) ) {
return next;
}
}
throw new IllegalArgumentException("No interceptor with class name " + name);
}
public void cleanup() {
//Nothing to do here.
}
}