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