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.weblogic;
023    
024    import java.io.IOException;
025    import java.lang.reflect.Method;
026    import java.util.concurrent.atomic.AtomicReference;
027    
028    import org.granite.gravity.AbstractChannel;
029    import org.granite.gravity.AsyncHttpContext;
030    import org.granite.gravity.Gravity;
031    import org.granite.logging.Logger;
032    
033    import weblogic.servlet.http.AbstractAsyncServlet;
034    import weblogic.servlet.http.RequestResponseKey;
035    
036    /**
037     * @author Franck WOLFF
038     */
039    public 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    }