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 javax.servlet.ServletConfig;
028    
029    import org.granite.gravity.AbstractChannel;
030    import org.granite.gravity.AsyncHttpContext;
031    import org.granite.gravity.GravityConfig;
032    import org.granite.logging.Logger;
033    
034    import weblogic.servlet.http.AbstractAsyncServlet;
035    import weblogic.servlet.http.RequestResponseKey;
036    
037    /**
038     * @author Franck WOLFF
039     */
040    public class WebLogicChannel extends AbstractChannel {
041    
042            private static final Logger log = Logger.getLogger(WebLogicChannel.class);
043            
044            // For WebLogic 9.1 compatibility.
045            private static Method isValid = null;
046            static {
047                    try {
048                            isValid = RequestResponseKey.class.getDeclaredMethod("isValid");
049                    }
050                    catch (Throwable t) {
051                    }
052            }
053        
054        private final AtomicReference<RequestResponseKey> key = new AtomicReference<RequestResponseKey>();
055    
056            public WebLogicChannel(ServletConfig servletConfig, GravityConfig gravityConfig, String id) {
057                    super(servletConfig, gravityConfig, id);
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                            RequestResponseKey key = this.key.getAndSet(null);
137                            if (key != null) {
138                                    try {
139                                            key.getResponse().getOutputStream().close();
140                                    }
141                                    catch (Exception e) {
142                                            log.debug(e, "Could not close key: %s for channel: %s", key, this);
143                                    }
144                            }
145                    }
146            }
147            
148            private static boolean isValid(RequestResponseKey key) {
149                    if (isValid != null) try {
150                            return (Boolean)isValid.invoke(key);
151                    }
152                    catch (Exception e) {
153                            return false;
154                    }
155                    return true;
156            }
157    }