All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cometd.server.transport.AbstractStreamHttpTransport Maven / Gradle / Ivy

There is a newer version: 8.0.6
Show newest version
/*
 * Copyright (c) 2008-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cometd.server.transport;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.cometd.bayeux.server.ServerMessage;
import org.cometd.server.BayeuxServerImpl;
import org.cometd.server.ServerSessionImpl;

/**
 * Abstract Long Polling Transport.
 * 

* Transports based on this class can be configured with servlet init parameters: *

*
browserId
The Cookie name used to save a browser ID.
*
maxSessionsPerBrowser
The maximum number of long polling sessions allowed per browser.
*
multiSessionInterval
The polling interval to use once max session per browser is exceeded.
*
autoBatch
If true a batch will be automatically created to span the handling of messages received from a session.
*
allowMultiSessionsNoBrowser
Allows multiple sessions even when the browser identifier cannot be retrieved.
*
*/ public abstract class AbstractStreamHttpTransport extends AbstractHttpTransport { private static final String SCHEDULER_ATTRIBUTE = "org.cometd.scheduler"; protected AbstractStreamHttpTransport(BayeuxServerImpl bayeux, String name) { super(bayeux, name); } @Override public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { getBayeux().setCurrentTransport(this); setCurrentRequest(request); try { process(request, response); } finally { setCurrentRequest(null); getBayeux().setCurrentTransport(null); } } protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { LongPollScheduler scheduler = (LongPollScheduler)request.getAttribute(SCHEDULER_ATTRIBUTE); if (scheduler == null) { // Not a resumed /meta/connect, process messages. try { ServerMessage.Mutable[] messages = parseMessages(request); processMessages(request, response, messages); } catch (ParseException x) { handleJSONParseException(request, response, x.getMessage(), x.getCause()); } } else { resume(request.getAsyncContext(), scheduler.getServerSession(), scheduler.getMetaConnectReply()); } } @Override protected HttpScheduler suspend(HttpServletRequest request, HttpServletResponse response, ServerSessionImpl session, ServerMessage.Mutable reply, String browserId, long timeout) { AsyncContext asyncContext = request.startAsync(request, response); asyncContext.setTimeout(0); HttpScheduler scheduler = newHttpScheduler(asyncContext, session, reply, browserId, timeout); request.setAttribute(SCHEDULER_ATTRIBUTE, scheduler); return scheduler; } protected HttpScheduler newHttpScheduler(AsyncContext asyncContext, ServerSessionImpl session, ServerMessage.Mutable reply, String browserId, long timeout) { return new DispatchingLongPollScheduler(asyncContext, session, reply, browserId, timeout); } protected abstract ServerMessage.Mutable[] parseMessages(HttpServletRequest request) throws IOException, ParseException; protected ServerMessage.Mutable[] parseMessages(String[] requestParameters) throws IOException, ParseException { if (requestParameters == null || requestParameters.length == 0) throw new IOException("Missing '" + MESSAGE_PARAM + "' request parameter"); if (requestParameters.length == 1) return parseMessages(requestParameters[0]); List messages = new ArrayList<>(); for (String batch : requestParameters) { if (batch == null) continue; messages.addAll(Arrays.asList(parseMessages(batch))); } return messages.toArray(new ServerMessage.Mutable[messages.size()]); } @Override @SuppressWarnings("ForLoopReplaceableByForEach") protected void write(HttpServletRequest request, HttpServletResponse response, ServerSessionImpl session, boolean startInterval, List messages, ServerMessage.Mutable[] replies) { try { ServletOutputStream output; try { output = beginWrite(request, response); // Write the messages first. for (int i = 0; i < messages.size(); ++i) { ServerMessage message = messages.get(i); if (i > 0) output.write(','); writeMessage(output, session, message); } } finally { // Start the interval timeout after writing the messages // since they may take time to be written, even in case // of exceptions to make sure the session can be swept. if (startInterval && session != null && session.isConnected()) session.startIntervalTimeout(getInterval()); } // Write the replies, if any. boolean needsComma = !messages.isEmpty(); for (int i = 0; i < replies.length; ++i) { ServerMessage reply = replies[i]; if (reply == null) continue; if (needsComma) output.write(','); needsComma = true; writeMessage(output, session, reply); } endWrite(output); } catch (Exception x) { error(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } protected void writeMessage(ServletOutputStream output, ServerSessionImpl session, ServerMessage message) throws IOException { output.print(message.getJSON()); } private void error(HttpServletResponse response, int responseCode) { error(null, response, responseCode); } protected abstract ServletOutputStream beginWrite(HttpServletRequest request, HttpServletResponse response) throws IOException; protected abstract void endWrite(ServletOutputStream output) throws IOException; protected class DispatchingLongPollScheduler extends LongPollScheduler { public DispatchingLongPollScheduler(AsyncContext asyncContext, ServerSessionImpl session, ServerMessage.Mutable reply, String browserId, long timeout) { super(asyncContext, session, reply, browserId, timeout); } protected void dispatch() { // We dispatch() when either we are suspended or timed out, instead of doing a write() + complete(). // If we have to write a message to 10 clients, and the first client write() blocks, then we would // be delaying the other 9 clients. // By always calling dispatch() we allow each write to be on its own thread, and it may block without // affecting other writes. // Only with Servlet 3.1 and standard asynchronous I/O we would be able to do write() + complete() // without blocking, and it will be much more efficient because there is no thread dispatching and // there will be more mechanical sympathy. getAsyncContext().dispatch(); } @Override protected void error(int code) { HttpServletResponse response = (HttpServletResponse)getAsyncContext().getResponse(); AbstractStreamHttpTransport.this.error(response, code); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy