001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2014 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.seam21; 023 024import java.lang.reflect.Method; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028 029import javax.servlet.http.HttpServletRequest; 030 031import org.granite.context.GraniteContext; 032import org.granite.logging.Logger; 033import org.granite.messaging.amf.process.AMF3MessageInterceptor; 034import org.granite.messaging.service.ServiceException; 035import org.granite.messaging.webapp.HttpGraniteContext; 036import org.granite.messaging.webapp.HttpServletRequestParamWrapper; 037import org.granite.messaging.webapp.ServletGraniteContext; 038import org.jboss.seam.contexts.Contexts; 039import org.jboss.seam.contexts.ServletLifecycle; 040import org.jboss.seam.core.Conversation; 041import org.jboss.seam.core.ConversationPropagation; 042import org.jboss.seam.core.Manager; 043import org.jboss.seam.international.StatusMessage; 044import org.jboss.seam.international.StatusMessages; 045import org.jboss.seam.servlet.ServletRequestSessionMap; 046import org.jboss.seam.util.Reflections; 047import org.jboss.seam.web.ServletContexts; 048 049import flex.messaging.messages.Message; 050 051 052public class Seam21Interceptor implements AMF3MessageInterceptor { 053 054 private static final Logger log = Logger.getLogger(Seam21Interceptor.class); 055 056 private static final String CONVERSATION_ID = "conversationId"; 057 private static final String PARENT_CONVERSATION_ID = "parentConversationId"; 058 private static final String IS_LONG_RUNNING_CONVERSATION = "isLongRunningConversation"; 059 private static final String WAS_LONG_RUNNING_CONVERSATION_ENDED = "wasLongRunningConversationEnded"; 060 private static final String WAS_LONG_RUNNING_CONVERSATION_CREATED = "wasLongRunningConversationCreated"; 061 private static final String MESSAGE_HEADER = "MESSAGE_HEADER"; 062 private static final String MSG_SEP = ":;:"; 063 064 065 /* (non-Javadoc) 066 * @see org.granite.messaging.amf.process.AMF3MessageInterceptor#before(flex.messaging.messages.Message) 067 */ 068 public void before(Message amfReqMessage) { 069 if (log.isTraceEnabled()) 070 log.trace("Pre processing of request message: %s", amfReqMessage); 071 072 try { 073 GraniteContext context = GraniteContext.getCurrentInstance(); 074 075 if (context instanceof ServletGraniteContext) { 076 log.debug("Creating custom HttpServletRequest wrapper"); 077 HttpServletRequestParamWrapper request = new HttpServletRequestParamWrapper(((HttpGraniteContext)context).getRequest()); 078 079 //Now export the headers - copy the headers to request object 080 exportHeaders(request, amfReqMessage); 081 082 //Time to initialize Seam Context 083 initializeSeamContext(request); 084 } 085 } 086 catch(Exception e) { 087 log.error(e, "Exception while pre processing the request message."); 088 throw new ServiceException("Error while pre processing the request message - " + e.getMessage()); 089 } 090 } 091 092 /* (non-Javadoc) 093 * @see org.granite.messaging.amf.process.AMF3MessageInterceptor#after(flex.messaging.messages.Message, flex.messaging.messages.Message) 094 */ 095 public void after(Message amfReqMessage, Message amfRespMessage) { 096 try { 097 if (log.isTraceEnabled()) 098 log.trace("Post processing of response message: %s", amfReqMessage); 099 100 if (GraniteContext.getCurrentInstance() instanceof ServletGraniteContext) { 101 try { 102 //Now time to set back the headers, always has one body 103 importHeaders(amfRespMessage); 104 } 105 finally { 106 //Time to destroy the seam context 107 destroySeamContext(); 108 } 109 } 110 } 111 catch (Exception e) { 112 log.error(e, "Exception while post processing the response message."); 113 throw new ServiceException("Error while post processing the response message - " + e.getMessage()); 114 } 115 } 116 117 /** 118 * Reads the AMF request header and populate them in the request object 119 * @param request - HttpServletRequestParamWrapper 120 * @param amf3RequestMessage 121 */ 122 protected void exportHeaders(HttpServletRequestParamWrapper request, Message amf3RequestMessage) { 123 //Read the headers from first body 124 Map<String, Object> headerMap = amf3RequestMessage.getHeaders(); 125 if (headerMap != null && headerMap.size() > 0) { 126 Iterator<String> headerKeys = headerMap.keySet().iterator(); 127 while (headerKeys.hasNext()) { 128 String key = headerKeys.next(); 129 String value = headerMap.get(key) == null ? null : headerMap.get(key).toString(); 130 if( value != null) { 131 request.setParameter(key, value); 132 } 133 } 134 } 135 } 136 137 /** 138 * Update the AMF response message with the conversationId and other parameters. 139 * @param amf3ResponseMessage 140 */ 141 protected void importHeaders(Message amf3ResponseMessage) { 142 if (amf3ResponseMessage != null) { 143 Conversation conversation = Conversation.instance(); 144 if (Contexts.getEventContext().isSet("org.granite.tide.conversation.wasLongRunning") && !conversation.isLongRunning()) 145 amf3ResponseMessage.setHeader(WAS_LONG_RUNNING_CONVERSATION_ENDED, true); 146 147 if (Contexts.getEventContext().isSet("org.granite.tide.conversation.wasCreated") && conversation.isLongRunning()) 148 amf3ResponseMessage.setHeader(WAS_LONG_RUNNING_CONVERSATION_CREATED, true); 149 150 log.debug("CONVERSATION_ID: %s", conversation.getId()); 151 amf3ResponseMessage.setHeader(CONVERSATION_ID, conversation.getId()); 152 153 log.debug("PARENT_CONVERSATION_ID: %s", conversation.getParentId()); 154 amf3ResponseMessage.setHeader(PARENT_CONVERSATION_ID, conversation.getParentId()); 155 156 log.debug("IS_LONG_RUNNING_CONVERSATION: %s", conversation.isLongRunning()); 157 amf3ResponseMessage.setHeader(IS_LONG_RUNNING_CONVERSATION, conversation.isLongRunning()); 158 159 log.debug("Processing the Status messages."); 160 processStatusMessages(amf3ResponseMessage); 161 } 162 } 163 164 /** 165 * Initialize the Seam Context 166 * @param request - HttpServletRequest 167 */ 168 protected void initializeSeamContext(HttpServletRequest request) { 169 log.debug("beginning request"); 170 171 ServletLifecycle.beginRequest(request); 172 ServletContexts.instance().setRequest(request); 173 174 // Force "conversationId" as parameter for GraniteDS requests 175 Manager.instance().setConversationIdParameter(CONVERSATION_ID); 176 restoreConversationId(); 177 String conversationId = ConversationPropagation.instance().getConversationId(); 178 Manager.instance().restoreConversation(); 179 ServletLifecycle.resumeConversation(request); 180 handleConversationPropagation(); 181 if (conversationId != null && !conversationId.equals(Manager.instance().getCurrentConversationId())) { 182 log.debug("Changed current conversation from %s to %s", Manager.instance().getCurrentConversationId(), conversationId); 183 Manager.instance().updateCurrentConversationId(conversationId); 184 } 185 else if (conversationId != null) 186 log.debug("Restored conversation %s", conversationId); 187 if (Manager.instance().isLongRunningConversation()) 188 Contexts.getEventContext().set("org.granite.tide.conversation.wasLongRunning", true); 189 190 // Force creation of the session 191 if (request.getSession(false) == null) 192 request.getSession(true); 193 194 if (Boolean.TRUE.toString().equals(request.getParameter("org.granite.tide.isFirstCall"))) 195 Contexts.getSessionContext().set("org.granite.tide.isFirstCall", Boolean.TRUE); 196 197 if (Boolean.TRUE.toString().equals(request.getParameter("org.granite.tide.isFirstConversationCall")) && Manager.instance().isLongRunningConversation()) 198 Contexts.getConversationContext().set("org.granite.tide.isFirstConversationCall", Boolean.TRUE); 199 } 200 201 /** 202 * Destroy the Seam Context 203 * @param request - HttpServletRequest 204 */ 205 private void destroySeamContext() { 206 // Flush current conversation metadata if needed 207 if (Manager.instance().isLongRunningConversation()) { 208 Conversation conversation = Conversation.instance(); 209 try { 210 Method method = conversation.getClass().getDeclaredMethod("flush"); 211 method.setAccessible(true); 212 Reflections.invoke(method, conversation); 213 } 214 catch (Exception e) { 215 log.error("Could not flush current long-running conversation " + conversation.getId(), e); 216 } 217 } 218 219 //Retrieve the stored request from Seam Servlet Context 220 Manager.instance().endRequest( new ServletRequestSessionMap(ServletContexts.getInstance().getRequest()) ); 221 ServletLifecycle.endRequest(ServletContexts.getInstance().getRequest()); 222 223 log.debug("ended request"); 224 } 225 226 227 /** 228 * Process the Status messages and sets to the response header. 229 * @param amf3ResponseMessage 230 */ 231 protected void processStatusMessages(Message amf3ResponseMessage) { 232 if (amf3ResponseMessage != null) { 233 StatusMessages statusMessages = StatusMessages.instance(); 234 if (statusMessages == null) 235 return; 236 237 try { 238 // Execute and get the messages (once again reflection hack to use protected methods) 239 Method m = StatusMessages.class.getDeclaredMethod("doRunTasks"); 240 m.setAccessible(true); 241 m.invoke(statusMessages); 242 243 Method m2 = StatusMessages.class.getDeclaredMethod("getMessages"); 244 m2.setAccessible(true); 245 @SuppressWarnings("unchecked") 246 List<StatusMessage> messages = (List<StatusMessage>)m2.invoke(statusMessages); 247 248 log.debug("Found Messages: %b", !messages.isEmpty()); 249 StringBuilder messagesBuf = new StringBuilder(); 250 for (StatusMessage msg : messages) { 251 log.debug("StatusMessage %s - %s", msg.getDetail(), msg.getSummary()); 252 messagesBuf.append(msg.getSummary()); 253 messagesBuf.append(MSG_SEP); 254 } 255 256 String messageStr = messagesBuf.toString().trim(); 257 258 if (messageStr.length() > 0) { 259 messageStr = messageStr.substring(0, messageStr.lastIndexOf(MSG_SEP)); 260 amf3ResponseMessage.setHeader(MESSAGE_HEADER, messageStr); 261 } 262 } 263 catch (Exception e) { 264 log.error("Could not get status messages", e); 265 } 266 } 267 } 268 269 /** 270 * 271 */ 272 protected void handleConversationPropagation() { 273 Manager.instance().handleConversationPropagation( ServletContexts.getInstance().getRequest().getParameterMap() ); 274 } 275 276 /** 277 * 278 */ 279 protected void restoreConversationId() { 280 ConversationPropagation.instance().restoreConversationId( ServletContexts.getInstance().getRequest().getParameterMap() ); 281 } 282}