001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.http.server;
018
019import java.io.IOException;
020import java.util.concurrent.atomic.AtomicBoolean;
021
022import javax.servlet.AsyncContext;
023import javax.servlet.AsyncEvent;
024import javax.servlet.AsyncListener;
025import javax.servlet.http.HttpServletRequest;
026import javax.servlet.http.HttpServletResponse;
027
028import org.springframework.lang.Nullable;
029import org.springframework.util.Assert;
030
031/**
032 * A {@link ServerHttpAsyncRequestControl} to use on Servlet containers (Servlet 3.0+).
033 *
034 * @author Rossen Stoyanchev
035 * @since 4.0
036 */
037public class ServletServerHttpAsyncRequestControl implements ServerHttpAsyncRequestControl, AsyncListener {
038
039        private static final long NO_TIMEOUT_VALUE = Long.MIN_VALUE;
040
041
042        private final ServletServerHttpRequest request;
043
044        private final ServletServerHttpResponse response;
045
046        @Nullable
047        private AsyncContext asyncContext;
048
049        private AtomicBoolean asyncCompleted = new AtomicBoolean(false);
050
051
052        /**
053         * Constructor accepting a request and response pair that are expected to be of type
054         * {@link ServletServerHttpRequest} and {@link ServletServerHttpResponse}
055         * respectively.
056         */
057        public ServletServerHttpAsyncRequestControl(ServletServerHttpRequest request, ServletServerHttpResponse response) {
058                Assert.notNull(request, "request is required");
059                Assert.notNull(response, "response is required");
060
061                Assert.isTrue(request.getServletRequest().isAsyncSupported(),
062                                "Async support must be enabled on a servlet and for all filters involved " +
063                                "in async request processing. This is done in Java code using the Servlet API " +
064                                "or by adding \"<async-supported>true</async-supported>\" to servlet and " +
065                                "filter declarations in web.xml. Also you must use a Servlet 3.0+ container");
066
067                this.request = request;
068                this.response = response;
069        }
070
071
072        @Override
073        public boolean isStarted() {
074                return (this.asyncContext != null && this.request.getServletRequest().isAsyncStarted());
075        }
076
077        @Override
078        public boolean isCompleted() {
079                return this.asyncCompleted.get();
080        }
081
082        @Override
083        public void start() {
084                start(NO_TIMEOUT_VALUE);
085        }
086
087        @Override
088        public void start(long timeout) {
089                Assert.state(!isCompleted(), "Async processing has already completed");
090                if (isStarted()) {
091                        return;
092                }
093
094                HttpServletRequest servletRequest = this.request.getServletRequest();
095                HttpServletResponse servletResponse = this.response.getServletResponse();
096
097                this.asyncContext = servletRequest.startAsync(servletRequest, servletResponse);
098                this.asyncContext.addListener(this);
099
100                if (timeout != NO_TIMEOUT_VALUE) {
101                        this.asyncContext.setTimeout(timeout);
102                }
103        }
104
105        @Override
106        public void complete() {
107                if (this.asyncContext != null && isStarted() && !isCompleted()) {
108                        this.asyncContext.complete();
109                }
110        }
111
112
113        // ---------------------------------------------------------------------
114        // Implementation of AsyncListener methods
115        // ---------------------------------------------------------------------
116
117        @Override
118        public void onComplete(AsyncEvent event) throws IOException {
119                this.asyncContext = null;
120                this.asyncCompleted.set(true);
121        }
122
123        @Override
124        public void onStartAsync(AsyncEvent event) throws IOException {
125        }
126
127        @Override
128        public void onError(AsyncEvent event) throws IOException {
129        }
130
131        @Override
132        public void onTimeout(AsyncEvent event) throws IOException {
133        }
134
135}