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 */
022package org.granite.gravity.weblogic;
023
024import java.io.IOException;
025import java.lang.reflect.Method;
026import java.util.concurrent.atomic.AtomicReference;
027
028import org.granite.gravity.AbstractChannel;
029import org.granite.gravity.AsyncHttpContext;
030import org.granite.gravity.Gravity;
031import org.granite.logging.Logger;
032
033import weblogic.servlet.http.AbstractAsyncServlet;
034import weblogic.servlet.http.RequestResponseKey;
035
036/**
037 * @author Franck WOLFF
038 */
039public class WebLogicChannel extends AbstractChannel {
040
041        private static final Logger log = Logger.getLogger(WebLogicChannel.class);
042        
043        // For WebLogic 9.1 compatibility.
044        private static Method isValid = null;
045        static {
046                try {
047                        isValid = RequestResponseKey.class.getDeclaredMethod("isValid");
048                }
049                catch (Throwable t) {
050                }
051        }
052    
053    private final AtomicReference<RequestResponseKey> key = new AtomicReference<RequestResponseKey>();
054
055    
056        public WebLogicChannel(Gravity gravity, String id, WebLogicChannelFactory factory, String clientType) {
057                super(gravity, id, factory, clientType);
058        }
059        
060        public void setRequestResponseKey(RequestResponseKey key) {
061        if (log.isDebugEnabled())
062            log.debug("Channel: %s got new asyncContext: %s", getId(), key);
063        
064        // Set this channel's request/response key.
065        RequestResponseKey previousKey = this.key.getAndSet(key);
066        
067        // Normally, we should have only two cases here:
068        //
069        // 1) this.key == null && key != null -> new (re)connect message.
070        // 2) this.key != null && key == null -> timeout.
071        //
072        // Not sure about what should be done if this.key != null && key != null, so
073        // warn about this case and close this.key if it is not the same as the key
074        // parameter.
075        if (previousKey != null) {
076                if (key != null) {
077                        log.warn(
078                                "Got a new non null key %s while current key %s isn't null",
079                                key, this.key.get()
080                        );
081                }
082                if (previousKey != key) {
083                        try {
084                                previousKey.getResponse().getOutputStream().close();
085                        }
086                        catch (Exception e) {
087                                log.debug(e, "Error while closing key");
088                        }
089                }
090        }
091        
092        // Try to queue receiver if the new asyncContext isn't null.
093        if (key != null)
094                queueReceiver();
095        }
096
097        @Override
098        protected boolean hasAsyncHttpContext() {
099                return key.get() != null;
100        }
101
102        @Override
103        protected AsyncHttpContext acquireAsyncHttpContext() {
104                RequestResponseKey key = this.key.getAndSet(null);
105                if (key == null || !isValid(key))
106                        return null;
107
108                try {
109                        AbstractAsyncServlet.notify(key, this);
110                }
111                catch (IOException e) {
112                        throw new RuntimeException(e);
113                }
114                
115                return null;
116        }
117
118        @Override
119        protected void releaseAsyncHttpContext(AsyncHttpContext context) {
120                // This method shouldn't be called in a WebLogic environment, anyway...
121                try {
122                        if (context != null)
123                                context.getResponse().getOutputStream().close();
124                }
125                catch (Exception e) {
126                        log.warn(e, "Could not release asyncHttpContext for channel: %s", this);
127                }
128        }
129
130        @Override
131        public void destroy() {
132                try {
133                        super.destroy();
134                }
135                finally {
136                        close();
137                }
138        }
139        
140        public void close() {
141                RequestResponseKey key = this.key.getAndSet(null);
142                if (key != null) {
143                        try {
144                                key.getResponse().getOutputStream().close();
145                        }
146                        catch (Exception e) {
147                                log.debug(e, "Could not close key: %s for channel: %s", key, this);
148                        }
149                }
150        }
151        
152        private static boolean isValid(RequestResponseKey key) {
153                if (isValid != null) try {
154                        return (Boolean)isValid.invoke(key);
155                }
156                catch (Exception e) {
157                        return false;
158                }
159                return true;
160        }
161}