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.gravity.servlet3;
023
024 import java.util.concurrent.atomic.AtomicReference;
025
026 import javax.servlet.AsyncContext;
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.Gravity;
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(Gravity gravity, String id, AsyncChannelFactory factory, String clientType) {
048 super(gravity, id, factory, clientType);
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 close();
157 }
158 }
159
160 public void close() {
161 AsyncContext asyncContext = this.asyncContext.getAndSet(null);
162 if (asyncContext != null) {
163 try {
164 asyncContext.complete();
165 }
166 catch (Exception e) {
167 log.debug(e, "Could not close asyncContext: %s for channel: %s", asyncContext, this);
168 }
169 }
170 }
171 }