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.gae;
023    
024    import java.io.IOException;
025    import java.util.List;
026    
027    import javax.servlet.ServletException;
028    import javax.servlet.http.HttpServletRequest;
029    import javax.servlet.http.HttpServletResponse;
030    
031    import org.granite.gravity.AbstractGravityServlet;
032    import org.granite.gravity.Gravity;
033    import org.granite.gravity.GravityManager;
034    import org.granite.logging.Logger;
035    
036    import com.google.apphosting.api.DeadlineExceededException;
037    
038    import flex.messaging.messages.AsyncMessage;
039    import flex.messaging.messages.Message;
040    
041    
042    /**
043     * @author William DRAI
044     */
045    public class GravityGAEServlet extends AbstractGravityServlet {
046    
047        private static final long serialVersionUID = 1L;
048    
049        private static final Logger log = Logger.getLogger(GravityGAEServlet.class);
050    
051    
052        private static long GAE_TIMEOUT = 20000L;
053        private static long GAE_POLLING_INTERVAL = 500L;
054    
055    
056        @Override
057        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
058            doPost(req,resp);
059        }
060    
061        @Override
062        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
063    
064                    Gravity gravity = GravityManager.getGravity(getServletContext());
065                    GAEChannelFactory channelFactory = new GAEChannelFactory((GAEGravity)gravity);
066                    
067                    try {
068                            // Setup context (thread local GraniteContext, etc.)
069                            initializeRequest(gravity, request, response);
070                            
071                            // New Request.
072                            Message[] amf3Requests = deserialize(gravity, request);
073    
074                log.debug(">> [AMF3 REQUESTS] %s", (Object)amf3Requests);
075    
076                Message[] amf3Responses = null;
077                
078                boolean accessed = false;
079                for (int i = 0; i < amf3Requests.length; i++) {
080                    Message amf3Request = amf3Requests[i];
081                    
082                    // Ask gravity to create a specific response (will be null for connect request from tunnel).
083                    Message amf3Response = gravity.handleMessage(channelFactory, amf3Request);
084                    String channelId = (String)amf3Request.getClientId();
085                    
086                    // Mark current channel (if any) as accessed.
087                    if (!accessed)
088                            accessed = gravity.access(channelId);
089                    
090                    // (Re)Connect message from tunnel.
091                    if (amf3Response == null) {
092                        if (amf3Requests.length > 1)
093                            throw new IllegalArgumentException("Only one request is allowed on tunnel.");
094    
095                            GAEChannel channel = gravity.getChannel(channelFactory, channelId);
096                            if (channel == null)
097                                    throw new NullPointerException("No channel on connect");
098                            
099                            long pollingInterval = gravity.getGravityConfig().getExtra().get("gae/@polling-interval", Long.TYPE, GAE_POLLING_INTERVAL);
100    
101                        long initialTime = System.currentTimeMillis();
102                        do {
103                            // Get messages or wait
104                            List<Message> messages = null;
105                            synchronized (channel) {
106                                // Get pending messages from client queue
107                                messages = channel.takeMessages();
108                            }
109            
110                            // Send the messages
111                            if (messages != null) {
112                                    amf3Responses = messages.toArray(new Message[0]);
113                                    ((AsyncMessage)amf3Responses[i]).setCorrelationId(amf3Requests[i].getMessageId());
114                                    break;
115                            }
116                            
117                            try {
118                                    Thread.sleep(pollingInterval);
119                            }
120                            catch (InterruptedException e) {
121                                    break;
122                            }
123                            catch (DeadlineExceededException e) {
124                                    break;
125                            }
126                        }
127                        while (System.currentTimeMillis()-initialTime < GAE_TIMEOUT);
128                        
129                        if (amf3Responses == null)
130                            amf3Responses = new Message[0];
131                    }
132                    else {
133                            if (amf3Responses == null)
134                                    amf3Responses = new Message[amf3Requests.length];
135                            amf3Responses[i] = amf3Response;
136                    }
137                }
138    
139                log.debug("<< [AMF3 RESPONSES] %s", (Object)amf3Responses);
140    
141                serialize(gravity, response, amf3Responses);
142                    }
143            catch (IOException e) {
144                log.error(e, "Gravity message error");
145                throw e;
146            }
147            catch (Exception e) {
148                log.error(e, "Gravity message error");
149                throw new ServletException(e);
150            }
151                    finally {
152                            // Cleanup context (thread local GraniteContext, etc.)
153                            cleanupRequest(request);
154                    }
155        }
156    }