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