/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* Granite Data Services is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Granite Data Services is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA, or see <http://www.gnu.org/licenses/>.
*/
package org.granite.cdi;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
import javax.enterprise.context.Conversation;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import org.granite.context.GraniteContext;
import org.granite.logging.Logger;
import org.granite.messaging.amf.process.AMF3MessageInterceptor;
import org.granite.messaging.service.ServiceException;
import org.granite.messaging.webapp.HttpGraniteContext;
import org.granite.messaging.webapp.HttpServletRequestParamWrapper;
import org.granite.messaging.webapp.ServletGraniteContext;
import org.granite.tide.cdi.ConversationState;
import org.granite.tide.cdi.EventState;
import org.granite.tide.cdi.SessionState;
import org.granite.util.TypeUtil;
import flex.messaging.messages.Message;
public class CDIInterceptor implements AMF3MessageInterceptor {
private static final Logger log = Logger.getLogger(CDIInterceptor.class);
private static final String CONVERSATION_ID = "conversationId";
private static final String IS_LONG_RUNNING_CONVERSATION = "isLongRunningConversation";
private static final String WAS_LONG_RUNNING_CONVERSATION_CREATED = "wasLongRunningConversationCreated";
private static final String WAS_LONG_RUNNING_CONVERSATION_ENDED = "wasLongRunningConversationEnded";
private final CDIConversationManager conversationManager;
private final ServletRequestListener requestListener;
public CDIInterceptor() {
/**
* Awful pile of hacks to detect the CDI container
* and implement the corresponding behaviour
*/
CDIConversationManager conversationManager = null;
ServletRequestListener listener = null;
try {
Thread.currentThread().getContextClassLoader().loadClass("org.jboss.weld.manager.BeanManagerLookupService");
listener = TypeUtil.newInstance("org.jboss.weld.servlet.WeldListener", ServletRequestListener.class);
if (listener instanceof ServletContextListener) {
ServletGraniteContext graniteContext = (ServletGraniteContext)GraniteContext.getCurrentInstance();
((ServletContextListener)listener).contextInitialized(new ServletContextEvent(graniteContext.getServletContext()));
}
conversationManager = TypeUtil.newInstance("org.granite.cdi.Weld11ConversationManager", CDIConversationManager.class);
log.info("Detected JBoss Weld 2.0+");
}
catch (Exception e1) {
try {
Thread.currentThread().getContextClassLoader().loadClass("org.jboss.weld.context.http.HttpConversationContext");
conversationManager = TypeUtil.newInstance("org.granite.cdi.Weld11ConversationManager", CDIConversationManager.class);
listener = TypeUtil.newInstance("org.jboss.weld.servlet.WeldListener", ServletRequestListener.class);
log.info("Detected JBoss Weld 1.1+");
}
catch (Exception e2) {
try {
conversationManager = null;
// Hacky way to initialize a request listener for OWB
listener = TypeUtil.newInstance("org.apache.webbeans.servlet.WebBeansConfigurationListener", ServletRequestListener.class);
Field wbcField = listener.getClass().getDeclaredField("webBeansContext");
wbcField.setAccessible(true);
Object webBeansContext = wbcField.get(listener);
Field lcField = listener.getClass().getDeclaredField("lifeCycle");
lcField.setAccessible(true);
Method wbcGetService = webBeansContext.getClass().getMethod("getService", Class.class);
Object lifecycle = wbcGetService.invoke(webBeansContext, TypeUtil.forName("org.apache.webbeans.spi.ContainerLifecycle"));
lcField.set(listener, lifecycle);
log.info("Detected Apache OpenWebBeans, conversation support disabled");
}
catch (Exception f2) {
log.warn("Unsupported CDI container, conversation support disabled");
}
}
}
this.conversationManager = conversationManager;
this.requestListener = listener;
}
private static final String MESSAGECOUNT_ATTR = CDIInterceptor.class.getName() + "_messageCount";
private static final String REQUESTWRAPPER_ATTR = CDIInterceptor.class.getName() + "_requestWrapper";
public void before(Message amf3RequestMessage) {
if (log.isTraceEnabled())
log.trace("Pre processing of request message: %s", amf3RequestMessage);
try {
GraniteContext context = GraniteContext.getCurrentInstance();
if (context instanceof HttpGraniteContext) {
HttpGraniteContext httpContext = ((HttpGraniteContext)context);
BeanManager beanManager = CDIUtils.lookupBeanManager(((HttpGraniteContext)context).getServletContext());
ServletRequestListener requestListener = getRequestListener();
if (requestListener != null) {
try {
Integer wrapCount = (Integer)httpContext.getRequest().getAttribute(MESSAGECOUNT_ATTR);
if (wrapCount == null) {
log.debug("Clearing default container request context");
ServletRequestEvent event = new ServletRequestEvent(httpContext.getServletContext(), httpContext.getRequest());
requestListener.requestDestroyed(event);
httpContext.getRequest().setAttribute(MESSAGECOUNT_ATTR, 1);
}
else
httpContext.getRequest().setAttribute(MESSAGECOUNT_ATTR, wrapCount+1);
}
catch (IllegalStateException e) {
// Weld 2.x ?
}
log.debug("Initializing wrapped AMF request");
HttpServletRequestParamWrapper requestWrapper = new HttpServletRequestParamWrapper(httpContext.getRequest());
httpContext.getRequest().setAttribute(REQUESTWRAPPER_ATTR, requestWrapper);
// Now export the headers - copy the headers to request object
Map<String, Object> headerMap = amf3RequestMessage.getHeaders();
if (headerMap != null && headerMap.size() > 0) {
Iterator<String> headerKeys = headerMap.keySet().iterator();
while (headerKeys.hasNext()) {
String key = headerKeys.next();
String value = headerMap.get(key) == null ? null : headerMap.get(key).toString();
if (value != null)
requestWrapper.setParameter(key, value);
}
}
ServletRequestEvent event = new ServletRequestEvent(((HttpGraniteContext)context).getServletContext(), requestWrapper);
requestListener.requestInitialized(event);
}
if (conversationManager != null) {
// Initialize CDI conversation context
String conversationId = (String)amf3RequestMessage.getHeader(CONVERSATION_ID);
Conversation conversation = conversationManager.initConversation(beanManager, conversationId);
if (conversation != null) {
@SuppressWarnings("unchecked")
Bean<EventState> eventBean = (Bean<EventState>)beanManager.getBeans(EventState.class).iterator().next();
EventState eventState = (EventState)beanManager.getReference(eventBean, EventState.class, beanManager.createCreationalContext(eventBean));
if (!conversation.isTransient())
eventState.setWasLongRunning(true);
if (conversationId != null && conversation.isTransient()) {
log.debug("Starting conversation " + conversationId);
conversation.begin(conversationId);
}
if (Boolean.TRUE.toString().equals(amf3RequestMessage.getHeader("org.granite.tide.isFirstConversationCall")) && !conversation.isTransient()) {
@SuppressWarnings("unchecked")
Bean<ConversationState> csBean = (Bean<ConversationState>)beanManager.getBeans(ConversationState.class).iterator().next();
((ConversationState)beanManager.getReference(csBean, ConversationState.class, beanManager.createCreationalContext(csBean))).setFirstCall(true);
}
}
}
if (Boolean.TRUE.toString().equals(amf3RequestMessage.getHeader("org.granite.tide.isFirstCall"))) {
@SuppressWarnings("unchecked")
Bean<SessionState> ssBean = (Bean<SessionState>)beanManager.getBeans(SessionState.class).iterator().next();
((SessionState)beanManager.getReference(ssBean, SessionState.class, beanManager.createCreationalContext(ssBean))).setFirstCall(true);
}
}
}
catch(Exception e) {
log.error(e, "Exception while pre processing the request message.");
throw new ServiceException("Error while pre processing the request message - " + e.getMessage());
}
}
public void after(Message amf3RequestMessage, Message amf3ResponseMessage) {
try {
if (log.isTraceEnabled())
log.trace("Post processing of response message: %s", amf3ResponseMessage);
GraniteContext context = GraniteContext.getCurrentInstance();
if (context instanceof HttpGraniteContext) {
BeanManager beanManager = CDIUtils.lookupBeanManager(((HttpGraniteContext)context).getServletContext());
try {
// Add conversation management headers to response
if (conversationManager != null && amf3ResponseMessage != null) {
@SuppressWarnings("unchecked")
Bean<Conversation> conversationBean = (Bean<Conversation>)beanManager.getBeans(Conversation.class).iterator().next();
Conversation conversation = (Conversation)beanManager.getReference(conversationBean, Conversation.class, beanManager.createCreationalContext(conversationBean));
if (conversation != null) {
@SuppressWarnings("unchecked")
Bean<EventState> eventBean = (Bean<EventState>)beanManager.getBeans(EventState.class).iterator().next();
EventState eventState = (EventState)beanManager.getReference(eventBean, EventState.class, beanManager.createCreationalContext(eventBean));
if (eventState.wasLongRunning() && !conversation.isTransient())
amf3ResponseMessage.setHeader(WAS_LONG_RUNNING_CONVERSATION_ENDED, true);
if (eventState.wasCreated() && !conversation.isTransient())
amf3ResponseMessage.setHeader(WAS_LONG_RUNNING_CONVERSATION_CREATED, true);
amf3ResponseMessage.setHeader(CONVERSATION_ID, conversation.getId());
amf3ResponseMessage.setHeader(IS_LONG_RUNNING_CONVERSATION, !conversation.isTransient());
}
}
}
finally {
if (conversationManager != null)
conversationManager.destroyConversation(beanManager);
HttpGraniteContext httpContext = ((HttpGraniteContext)context);
ServletRequestListener requestListener = getRequestListener();
if (requestListener != null) {
// Destroy the CDI context
HttpServletRequestParamWrapper requestWrapper = (HttpServletRequestParamWrapper)httpContext.getRequest().getAttribute(REQUESTWRAPPER_ATTR);
httpContext.getRequest().removeAttribute(REQUESTWRAPPER_ATTR);
ServletRequestEvent event = new ServletRequestEvent(httpContext.getServletContext(), requestWrapper);
requestListener.requestDestroyed(event);
log.debug("Destroying wrapped CDI AMF request");
Integer wrapCount = (Integer)httpContext.getRequest().getAttribute(MESSAGECOUNT_ATTR);
if (wrapCount == 1) {
log.debug("Restoring default container request context");
event = new ServletRequestEvent(((HttpGraniteContext)context).getServletContext(), httpContext.getRequest());
requestListener.requestInitialized(event);
httpContext.getRequest().removeAttribute(MESSAGECOUNT_ATTR);
}
else
httpContext.getRequest().setAttribute(MESSAGECOUNT_ATTR, wrapCount-1);
}
}
}
}
catch (Exception e) {
log.error(e, "Exception while post processing the response message.");
throw new ServiceException("Error while post processing the response message - " + e.getMessage());
}
}
private ServletRequestListener getRequestListener() {
return requestListener;
}
}