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