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