001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.cdi; 023 024import java.lang.reflect.Field; 025import java.lang.reflect.Method; 026import java.util.Iterator; 027import java.util.Map; 028 029import javax.enterprise.context.Conversation; 030import javax.enterprise.inject.spi.Bean; 031import javax.enterprise.inject.spi.BeanManager; 032import javax.servlet.ServletRequestEvent; 033import javax.servlet.ServletRequestListener; 034 035import org.granite.context.GraniteContext; 036import org.granite.logging.Logger; 037import org.granite.messaging.amf.process.AMF3MessageInterceptor; 038import org.granite.messaging.service.ServiceException; 039import org.granite.messaging.webapp.HttpGraniteContext; 040import org.granite.messaging.webapp.HttpServletRequestParamWrapper; 041import org.granite.messaging.webapp.ServletGraniteContext; 042import org.granite.tide.cdi.ConversationState; 043import org.granite.tide.cdi.EventState; 044import org.granite.tide.cdi.SessionState; 045import org.granite.util.TypeUtil; 046 047import flex.messaging.messages.Message; 048 049 050public class CDIInterceptor implements AMF3MessageInterceptor { 051 052 private static final Logger log = Logger.getLogger(CDIInterceptor.class); 053 054 private static final String CONVERSATION_ID = "conversationId"; 055 private static final String IS_LONG_RUNNING_CONVERSATION = "isLongRunningConversation"; 056 private static final String WAS_LONG_RUNNING_CONVERSATION_CREATED = "wasLongRunningConversationCreated"; 057 private static final String WAS_LONG_RUNNING_CONVERSATION_ENDED = "wasLongRunningConversationEnded"; 058 059 private final CDIConversationManager conversationManager; 060 private final ServletRequestListener requestListener; 061 062 063 public CDIInterceptor() { 064 CDIConversationManager conversationManager = null; 065 ServletRequestListener listener = null; 066 try { 067 Thread.currentThread().getContextClassLoader().loadClass("org.jboss.weld.context.http.HttpConversationContext"); 068 conversationManager = TypeUtil.newInstance("org.granite.cdi.Weld11ConversationManager", CDIConversationManager.class); 069 listener = TypeUtil.newInstance("org.jboss.weld.servlet.WeldListener", ServletRequestListener.class); 070 log.info("Detected JBoss Weld 1.1+"); 071 } 072 catch (Exception e) { 073 try { 074 conversationManager = null; 075 // Hacky way to initialize a request listener for OWB 076 listener = TypeUtil.newInstance("org.apache.webbeans.servlet.WebBeansConfigurationListener", ServletRequestListener.class); 077 Field wbcField = listener.getClass().getDeclaredField("webBeansContext"); 078 wbcField.setAccessible(true); 079 Object webBeansContext = wbcField.get(listener); 080 Field lcField = listener.getClass().getDeclaredField("lifeCycle"); 081 lcField.setAccessible(true); 082 Method wbcGetService = webBeansContext.getClass().getMethod("getService", Class.class); 083 Object lifecycle = wbcGetService.invoke(webBeansContext, TypeUtil.forName("org.apache.webbeans.spi.ContainerLifecycle")); 084 lcField.set(listener, lifecycle); 085 log.info("Detected Apache OpenWebBeans, conversation support disabled"); 086 } 087 catch (Exception f2) { 088 log.warn("Unsupported CDI container, conversation support disabled"); 089 } 090 } 091 this.conversationManager = conversationManager; 092 this.requestListener = listener; 093 } 094 095 096 private static final String MESSAGECOUNT_ATTR = CDIInterceptor.class.getName() + "_messageCount"; 097 private static final String REQUESTWRAPPER_ATTR = CDIInterceptor.class.getName() + "_requestWrapper"; 098 099 100 public void before(Message amf3RequestMessage) { 101 if (log.isTraceEnabled()) 102 log.trace("Pre processing of request message: %s", amf3RequestMessage); 103 104 try { 105 GraniteContext context = GraniteContext.getCurrentInstance(); 106 107 if (context instanceof HttpGraniteContext) { 108 HttpGraniteContext httpContext = ((HttpGraniteContext)context); 109 110 if (requestListener != null) { 111 Integer wrapCount = (Integer)httpContext.getRequest().getAttribute(MESSAGECOUNT_ATTR); 112 if (wrapCount == null) { 113 log.debug("Clearing default container request context"); 114 ServletRequestEvent event = new ServletRequestEvent(httpContext.getServletContext(), httpContext.getRequest()); 115 requestListener.requestDestroyed(event); 116 httpContext.getRequest().setAttribute(MESSAGECOUNT_ATTR, 1); 117 } 118 else 119 httpContext.getRequest().setAttribute(MESSAGECOUNT_ATTR, wrapCount+1); 120 121 log.debug("Initializing wrapped AMF request"); 122 123 HttpServletRequestParamWrapper requestWrapper = new HttpServletRequestParamWrapper(httpContext.getRequest()); 124 httpContext.getRequest().setAttribute(REQUESTWRAPPER_ATTR, requestWrapper); 125 126 // Now export the headers - copy the headers to request object 127 Map<String, Object> headerMap = amf3RequestMessage.getHeaders(); 128 if (headerMap != null && headerMap.size() > 0) { 129 Iterator<String> headerKeys = headerMap.keySet().iterator(); 130 while (headerKeys.hasNext()) { 131 String key = headerKeys.next(); 132 String value = headerMap.get(key) == null ? null : headerMap.get(key).toString(); 133 if (value != null) 134 requestWrapper.setParameter(key, value); 135 } 136 } 137 138 ServletRequestEvent event = new ServletRequestEvent(((HttpGraniteContext)context).getServletContext(), requestWrapper); 139 requestListener.requestInitialized(event); 140 } 141 142 BeanManager beanManager = CDIUtils.lookupBeanManager(((HttpGraniteContext)context).getServletContext()); 143 144 if (conversationManager != null) { 145 // Initialize CDI conversation context 146 String conversationId = (String)amf3RequestMessage.getHeader(CONVERSATION_ID); 147 148 Conversation conversation = conversationManager.initConversation(beanManager, conversationId); 149 150 @SuppressWarnings("unchecked") 151 Bean<EventState> eventBean = (Bean<EventState>)beanManager.getBeans(EventState.class).iterator().next(); 152 EventState eventState = (EventState)beanManager.getReference(eventBean, EventState.class, beanManager.createCreationalContext(eventBean)); 153 if (!conversation.isTransient()) 154 eventState.setWasLongRunning(true); 155 156 if (conversationId != null && conversation.isTransient()) { 157 log.debug("Starting conversation " + conversationId); 158 conversation.begin(conversationId); 159 } 160 161 if (Boolean.TRUE.toString().equals(amf3RequestMessage.getHeader("org.granite.tide.isFirstConversationCall")) && !conversation.isTransient()) { 162 @SuppressWarnings("unchecked") 163 Bean<ConversationState> csBean = (Bean<ConversationState>)beanManager.getBeans(ConversationState.class).iterator().next(); 164 ((ConversationState)beanManager.getReference(csBean, ConversationState.class, beanManager.createCreationalContext(csBean))).setFirstCall(true); 165 } 166 } 167 168 if (Boolean.TRUE.toString().equals(amf3RequestMessage.getHeader("org.granite.tide.isFirstCall"))) { 169 @SuppressWarnings("unchecked") 170 Bean<SessionState> ssBean = (Bean<SessionState>)beanManager.getBeans(SessionState.class).iterator().next(); 171 ((SessionState)beanManager.getReference(ssBean, SessionState.class, beanManager.createCreationalContext(ssBean))).setFirstCall(true); 172 } 173 } 174 } 175 catch(Exception e) { 176 log.error(e, "Exception while pre processing the request message."); 177 throw new ServiceException("Error while pre processing the request message - " + e.getMessage()); 178 } 179 } 180 181 182 public void after(Message amf3RequestMessage, Message amf3ResponseMessage) { 183 try { 184 if (log.isTraceEnabled()) 185 log.trace("Post processing of response message: %s", amf3ResponseMessage); 186 187 GraniteContext context = GraniteContext.getCurrentInstance(); 188 189 if (context instanceof HttpGraniteContext) { 190 BeanManager beanManager = CDIUtils.lookupBeanManager(((HttpGraniteContext)context).getServletContext()); 191 192 try { 193 // Add conversation management headers to response 194 if (conversationManager != null && amf3ResponseMessage != null) { 195 @SuppressWarnings("unchecked") 196 Bean<Conversation> conversationBean = (Bean<Conversation>)beanManager.getBeans(Conversation.class).iterator().next(); 197 Conversation conversation = (Conversation)beanManager.getReference(conversationBean, Conversation.class, beanManager.createCreationalContext(conversationBean)); 198 199 @SuppressWarnings("unchecked") 200 Bean<EventState> eventBean = (Bean<EventState>)beanManager.getBeans(EventState.class).iterator().next(); 201 EventState eventState = (EventState)beanManager.getReference(eventBean, EventState.class, beanManager.createCreationalContext(eventBean)); 202 if (eventState.wasLongRunning() && !conversation.isTransient()) 203 amf3ResponseMessage.setHeader(WAS_LONG_RUNNING_CONVERSATION_ENDED, true); 204 205 if (eventState.wasCreated() && !conversation.isTransient()) 206 amf3ResponseMessage.setHeader(WAS_LONG_RUNNING_CONVERSATION_CREATED, true); 207 208 amf3ResponseMessage.setHeader(CONVERSATION_ID, conversation.getId()); 209 210 amf3ResponseMessage.setHeader(IS_LONG_RUNNING_CONVERSATION, !conversation.isTransient()); 211 } 212 } 213 finally { 214 if (conversationManager != null) 215 conversationManager.destroyConversation(beanManager); 216 217 HttpGraniteContext httpContext = ((HttpGraniteContext)context); 218 219 if (requestListener != null) { 220 // Destroy the CDI context 221 HttpServletRequestParamWrapper requestWrapper = (HttpServletRequestParamWrapper)httpContext.getRequest().getAttribute(REQUESTWRAPPER_ATTR); 222 httpContext.getRequest().removeAttribute(REQUESTWRAPPER_ATTR); 223 ServletRequestEvent event = new ServletRequestEvent(httpContext.getServletContext(), requestWrapper); 224 requestListener.requestDestroyed(event); 225 226 log.debug("Destroying wrapped CDI AMF request"); 227 228 Integer wrapCount = (Integer)httpContext.getRequest().getAttribute(MESSAGECOUNT_ATTR); 229 if (wrapCount == 1) { 230 log.debug("Restoring default container request context"); 231 event = new ServletRequestEvent(((HttpGraniteContext)context).getServletContext(), httpContext.getRequest()); 232 requestListener.requestInitialized(event); 233 httpContext.getRequest().removeAttribute(MESSAGECOUNT_ATTR); 234 } 235 else 236 httpContext.getRequest().setAttribute(MESSAGECOUNT_ATTR, wrapCount-1); 237 } 238 } 239 } 240 } 241 catch (Exception e) { 242 log.error(e, "Exception while post processing the response message."); 243 throw new ServiceException("Error while post processing the response message - " + e.getMessage()); 244 } 245 } 246}