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.tide.cdi.ConversationState; 042import org.granite.tide.cdi.EventState; 043import org.granite.tide.cdi.SessionState; 044import org.granite.util.TypeUtil; 045 046import flex.messaging.messages.Message; 047 048 049public class CDIInterceptor implements AMF3MessageInterceptor { 050 051 private static final Logger log = Logger.getLogger(CDIInterceptor.class); 052 053 private static final String CONVERSATION_ID = "conversationId"; 054 private static final String IS_LONG_RUNNING_CONVERSATION = "isLongRunningConversation"; 055 private static final String WAS_LONG_RUNNING_CONVERSATION_CREATED = "wasLongRunningConversationCreated"; 056 private static final String WAS_LONG_RUNNING_CONVERSATION_ENDED = "wasLongRunningConversationEnded"; 057 058 private final CDIConversationManager conversationManager; 059 private final ServletRequestListener requestListener; 060 061 062 public CDIInterceptor() { 063 CDIConversationManager conversationManager = null; 064 ServletRequestListener listener = null; 065 try { 066 Thread.currentThread().getContextClassLoader().loadClass("org.jboss.weld.context.http.HttpConversationContext"); 067 conversationManager = TypeUtil.newInstance("org.granite.cdi.Weld11ConversationManager", CDIConversationManager.class); 068 listener = TypeUtil.newInstance("org.jboss.weld.servlet.WeldListener", ServletRequestListener.class); 069 log.info("Detected JBoss Weld 1.1+"); 070 } 071 catch (Exception e) { 072 try { 073 conversationManager = null; 074 // Hacky way to initialize a request listener for OWB 075 listener = TypeUtil.newInstance("org.apache.webbeans.servlet.WebBeansConfigurationListener", ServletRequestListener.class); 076 Field wbcField = listener.getClass().getDeclaredField("webBeansContext"); 077 wbcField.setAccessible(true); 078 Object webBeansContext = wbcField.get(listener); 079 Field lcField = listener.getClass().getDeclaredField("lifeCycle"); 080 lcField.setAccessible(true); 081 Method wbcGetService = webBeansContext.getClass().getMethod("getService", Class.class); 082 Object lifecycle = wbcGetService.invoke(webBeansContext, TypeUtil.forName("org.apache.webbeans.spi.ContainerLifecycle")); 083 lcField.set(listener, lifecycle); 084 log.info("Detected Apache OpenWebBeans, conversation support disabled"); 085 } 086 catch (Exception f2) { 087 log.warn("Unsupported CDI container, conversation support disabled"); 088 } 089 } 090 this.conversationManager = conversationManager; 091 this.requestListener = listener; 092 } 093 094 095 private static final String MESSAGECOUNT_ATTR = CDIInterceptor.class.getName() + "_messageCount"; 096 private static final String REQUESTWRAPPER_ATTR = CDIInterceptor.class.getName() + "_requestWrapper"; 097 098 099 public void before(Message amf3RequestMessage) { 100 if (log.isTraceEnabled()) 101 log.trace("Pre processing of request message: %s", amf3RequestMessage); 102 103 try { 104 GraniteContext context = GraniteContext.getCurrentInstance(); 105 106 if (context instanceof HttpGraniteContext) { 107 HttpGraniteContext httpContext = ((HttpGraniteContext)context); 108 109 if (requestListener != null) { 110 Integer wrapCount = (Integer)httpContext.getRequest().getAttribute(MESSAGECOUNT_ATTR); 111 if (wrapCount == null) { 112 log.debug("Clearing default container request context"); 113 ServletRequestEvent event = new ServletRequestEvent(httpContext.getServletContext(), httpContext.getRequest()); 114 requestListener.requestDestroyed(event); 115 httpContext.getRequest().setAttribute(MESSAGECOUNT_ATTR, 1); 116 } 117 else 118 httpContext.getRequest().setAttribute(MESSAGECOUNT_ATTR, wrapCount+1); 119 120 log.debug("Initializing wrapped AMF request"); 121 122 HttpServletRequestParamWrapper requestWrapper = new HttpServletRequestParamWrapper(httpContext.getRequest()); 123 httpContext.getRequest().setAttribute(REQUESTWRAPPER_ATTR, requestWrapper); 124 125 // Now export the headers - copy the headers to request object 126 Map<String, Object> headerMap = amf3RequestMessage.getHeaders(); 127 if (headerMap != null && headerMap.size() > 0) { 128 Iterator<String> headerKeys = headerMap.keySet().iterator(); 129 while (headerKeys.hasNext()) { 130 String key = headerKeys.next(); 131 String value = headerMap.get(key) == null ? null : headerMap.get(key).toString(); 132 if (value != null) 133 requestWrapper.setParameter(key, value); 134 } 135 } 136 137 ServletRequestEvent event = new ServletRequestEvent(((HttpGraniteContext)context).getServletContext(), requestWrapper); 138 requestListener.requestInitialized(event); 139 } 140 141 BeanManager beanManager = CDIUtils.lookupBeanManager(((HttpGraniteContext)context).getServletContext()); 142 143 if (conversationManager != null) { 144 // Initialize CDI conversation context 145 String conversationId = (String)amf3RequestMessage.getHeader(CONVERSATION_ID); 146 147 Conversation conversation = conversationManager.initConversation(beanManager, conversationId); 148 149 @SuppressWarnings("unchecked") 150 Bean<EventState> eventBean = (Bean<EventState>)beanManager.getBeans(EventState.class).iterator().next(); 151 EventState eventState = (EventState)beanManager.getReference(eventBean, EventState.class, beanManager.createCreationalContext(eventBean)); 152 if (!conversation.isTransient()) 153 eventState.setWasLongRunning(true); 154 155 if (conversationId != null && conversation.isTransient()) { 156 log.debug("Starting conversation " + conversationId); 157 conversation.begin(conversationId); 158 } 159 160 if (Boolean.TRUE.toString().equals(amf3RequestMessage.getHeader("org.granite.tide.isFirstConversationCall")) && !conversation.isTransient()) { 161 @SuppressWarnings("unchecked") 162 Bean<ConversationState> csBean = (Bean<ConversationState>)beanManager.getBeans(ConversationState.class).iterator().next(); 163 ((ConversationState)beanManager.getReference(csBean, ConversationState.class, beanManager.createCreationalContext(csBean))).setFirstCall(true); 164 } 165 } 166 167 if (Boolean.TRUE.toString().equals(amf3RequestMessage.getHeader("org.granite.tide.isFirstCall"))) { 168 @SuppressWarnings("unchecked") 169 Bean<SessionState> ssBean = (Bean<SessionState>)beanManager.getBeans(SessionState.class).iterator().next(); 170 ((SessionState)beanManager.getReference(ssBean, SessionState.class, beanManager.createCreationalContext(ssBean))).setFirstCall(true); 171 } 172 } 173 } 174 catch(Exception e) { 175 log.error(e, "Exception while pre processing the request message."); 176 throw new ServiceException("Error while pre processing the request message - " + e.getMessage()); 177 } 178 } 179 180 181 public void after(Message amf3RequestMessage, Message amf3ResponseMessage) { 182 try { 183 if (log.isTraceEnabled()) 184 log.trace("Post processing of response message: %s", amf3ResponseMessage); 185 186 GraniteContext context = GraniteContext.getCurrentInstance(); 187 188 if (context instanceof HttpGraniteContext) { 189 BeanManager beanManager = CDIUtils.lookupBeanManager(((HttpGraniteContext)context).getServletContext()); 190 191 try { 192 // Add conversation management headers to response 193 if (conversationManager != null && amf3ResponseMessage != null) { 194 @SuppressWarnings("unchecked") 195 Bean<Conversation> conversationBean = (Bean<Conversation>)beanManager.getBeans(Conversation.class).iterator().next(); 196 Conversation conversation = (Conversation)beanManager.getReference(conversationBean, Conversation.class, beanManager.createCreationalContext(conversationBean)); 197 198 @SuppressWarnings("unchecked") 199 Bean<EventState> eventBean = (Bean<EventState>)beanManager.getBeans(EventState.class).iterator().next(); 200 EventState eventState = (EventState)beanManager.getReference(eventBean, EventState.class, beanManager.createCreationalContext(eventBean)); 201 if (eventState.wasLongRunning() && !conversation.isTransient()) 202 amf3ResponseMessage.setHeader(WAS_LONG_RUNNING_CONVERSATION_ENDED, true); 203 204 if (eventState.wasCreated() && !conversation.isTransient()) 205 amf3ResponseMessage.setHeader(WAS_LONG_RUNNING_CONVERSATION_CREATED, true); 206 207 amf3ResponseMessage.setHeader(CONVERSATION_ID, conversation.getId()); 208 209 amf3ResponseMessage.setHeader(IS_LONG_RUNNING_CONVERSATION, !conversation.isTransient()); 210 } 211 } 212 finally { 213 if (conversationManager != null) 214 conversationManager.destroyConversation(beanManager); 215 216 HttpGraniteContext httpContext = ((HttpGraniteContext)context); 217 218 if (requestListener != null) { 219 // Destroy the CDI context 220 HttpServletRequestParamWrapper requestWrapper = (HttpServletRequestParamWrapper)httpContext.getRequest().getAttribute(REQUESTWRAPPER_ATTR); 221 httpContext.getRequest().removeAttribute(REQUESTWRAPPER_ATTR); 222 ServletRequestEvent event = new ServletRequestEvent(httpContext.getServletContext(), requestWrapper); 223 requestListener.requestDestroyed(event); 224 225 log.debug("Destroying wrapped CDI AMF request"); 226 227 Integer wrapCount = (Integer)httpContext.getRequest().getAttribute(MESSAGECOUNT_ATTR); 228 if (wrapCount == 1) { 229 log.debug("Restoring default container request context"); 230 event = new ServletRequestEvent(((HttpGraniteContext)context).getServletContext(), httpContext.getRequest()); 231 requestListener.requestInitialized(event); 232 httpContext.getRequest().removeAttribute(MESSAGECOUNT_ATTR); 233 } 234 else 235 httpContext.getRequest().setAttribute(MESSAGECOUNT_ATTR, wrapCount-1); 236 } 237 } 238 } 239 } 240 catch (Exception e) { 241 log.error(e, "Exception while post processing the response message."); 242 throw new ServiceException("Error while post processing the response message - " + e.getMessage()); 243 } 244 } 245}