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 */
022 package org.granite.cdi;
023
024 import java.lang.reflect.Field;
025 import java.lang.reflect.Method;
026 import java.util.Iterator;
027 import java.util.Map;
028
029 import javax.enterprise.context.Conversation;
030 import javax.enterprise.inject.spi.Bean;
031 import javax.enterprise.inject.spi.BeanManager;
032 import javax.servlet.ServletRequestEvent;
033 import javax.servlet.ServletRequestListener;
034
035 import org.granite.context.GraniteContext;
036 import org.granite.logging.Logger;
037 import org.granite.messaging.amf.process.AMF3MessageInterceptor;
038 import org.granite.messaging.service.ServiceException;
039 import org.granite.messaging.webapp.HttpGraniteContext;
040 import org.granite.messaging.webapp.HttpServletRequestParamWrapper;
041 import org.granite.tide.cdi.ConversationState;
042 import org.granite.tide.cdi.EventState;
043 import org.granite.tide.cdi.SessionState;
044 import org.granite.util.TypeUtil;
045
046 import flex.messaging.messages.Message;
047
048
049 public 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 }