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.gravity.servlet3;
022
023 import java.util.concurrent.atomic.AtomicReference;
024
025 import javax.servlet.AsyncContext;
026 import javax.servlet.ServletConfig;
027 import javax.servlet.http.HttpServletRequest;
028 import javax.servlet.http.HttpServletResponse;
029
030 import org.granite.gravity.AbstractChannel;
031 import org.granite.gravity.AbstractGravityServlet;
032 import org.granite.gravity.AsyncHttpContext;
033 import org.granite.gravity.GravityConfig;
034 import org.granite.logging.Logger;
035
036 import flex.messaging.messages.Message;
037
038 /**
039 * @author Franck WOLFF
040 */
041 public class AsyncChannel extends AbstractChannel {
042
043 private static final Logger log = Logger.getLogger(AsyncChannel.class);
044
045 private final AtomicReference<AsyncContext> asyncContext = new AtomicReference<AsyncContext>();
046
047 public AsyncChannel(ServletConfig servletConfig, GravityConfig gravityConfig, String id) {
048 super(servletConfig, gravityConfig, id);
049 }
050
051 public void setAsyncContext(AsyncContext asyncContext) {
052 if (log.isDebugEnabled())
053 log.debug("Channel: %s got new asyncContext: %s", getId(), asyncContext);
054
055 // Set this channel's async context.
056 AsyncContext previousAsyncContext = this.asyncContext.getAndSet(asyncContext);
057
058 // Normally, we should have only two cases here:
059 //
060 // 1) this.asyncContext == null && asyncContext != null -> new (re)connect message.
061 // 2) this.asyncContext != null && asyncContext == null -> timeout.
062 //
063 // Not sure about what should be done if this.asyncContext != null && asyncContext != null, so
064 // warn about this case and close this.asyncContext if it is not the same as the asyncContext
065 // parameter.
066 if (previousAsyncContext != null) {
067 if (asyncContext != null) {
068 log.warn(
069 "Got a new non null asyncContext %s while current asyncContext %s isn't null",
070 asyncContext, this.asyncContext.get()
071 );
072 }
073 if (previousAsyncContext != asyncContext) {
074 try {
075 previousAsyncContext.complete();
076 }
077 catch (Exception e) {
078 log.debug(e, "Error while closing asyncContext");
079 }
080 }
081 }
082
083 // Try to queue receiver if the new asyncContext isn't null.
084 if (asyncContext != null)
085 queueReceiver();
086 }
087
088 @Override
089 protected boolean hasAsyncHttpContext() {
090 return asyncContext.get() != null;
091 }
092
093 @Override
094 protected AsyncHttpContext acquireAsyncHttpContext() {
095
096 AsyncContext asyncContext = this.asyncContext.getAndSet(null);
097 if (asyncContext == null)
098 return null;
099
100 AsyncHttpContext context = null;
101
102 try {
103 HttpServletRequest request = null;
104 HttpServletResponse response = null;
105 try {
106 request = (HttpServletRequest)asyncContext.getRequest();
107 response = (HttpServletResponse)asyncContext.getResponse();
108 } catch (Exception e) {
109 log.warn("Illegal asyncContext: %s", asyncContext);
110 return null;
111 }
112 if (request == null || response == null) {
113 log.warn("Illegal asyncContext (request or response is null): %s", asyncContext);
114 return null;
115 }
116
117 Message requestMessage = AbstractGravityServlet.getConnectMessage(request);
118 if (requestMessage == null) {
119 log.warn("No request message while running channel: %s", getId());
120 return null;
121 }
122
123 context = new AsyncHttpContext(request, response, requestMessage, asyncContext);
124 }
125 finally {
126 if (context == null) {
127 try {
128 asyncContext.complete();
129 }
130 catch (Exception e) {
131 log.debug(e, "Error while closing asyncContext: %s", asyncContext);
132 }
133 }
134 }
135
136 return context;
137 }
138
139 @Override
140 protected void releaseAsyncHttpContext(AsyncHttpContext context) {
141 try {
142 if (context != null && context.getObject() != null)
143 ((AsyncContext)context.getObject()).complete();
144 }
145 catch (Exception e) {
146 log.warn(e, "Could not release asyncContext for channel: %s", this);
147 }
148 }
149
150 @Override
151 public void destroy() {
152 try {
153 super.destroy();
154 }
155 finally {
156 AsyncContext asyncContext = this.asyncContext.getAndSet(null);
157 if (asyncContext != null) {
158 try {
159 asyncContext.complete();
160 }
161 catch (Exception e) {
162 log.debug(e, "Could not close asyncContext: %s for channel: %s", asyncContext, this);
163 }
164 }
165 }
166 }
167 }