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    }