Java tutorial
/* * Copyright 2010-2012 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.cloudfoundry.tools.timeout; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletResponse; import org.cloudfoundry.tools.timeout.monitor.HttpServletResponseMonitor; import org.cloudfoundry.tools.timeout.monitor.HttpServletResponseMonitorFactory; import org.cloudfoundry.tools.timeout.monitor.ReplayableHttpServletResponseMonitor; import org.cloudfoundry.tools.timeout.monitor.ReplayableHttpServletResponseMonitorFactory; import org.springframework.http.HttpStatus; import org.springframework.util.Assert; /** * {@link TimeoutProtectionStrategy} that works by recording the original request such that it can be replayed to a * subsequent poll request. Requests that take longer than the {@link #setThreshold(long) threshold} to respond will be * recorded. The threshold should therefore be set to a value slightly lower than the expected gateway timeout. The * {@link #setFailTimeout(long)} method should be used to the timeout that will protect against requests that never * receive a poll (for example due to network failure). The {@link #setLongPollTime(long)} method can be used to set the * long-poll time for the poll request. This value should obviously be less than the gateway timeout. * <p> * This strategy consumes more memory than {@link HotSwappingTimeoutProtectionStrategy} but does not require that * timeouts only occur. * * @author Phillip Webb */ public class ReplayingTimeoutProtectionStrategy implements TimeoutProtectionStrategy, TimeoutValues { private long threshold = TimeUnit.SECONDS.toMillis(14); private long longPollTime = TimeUnit.SECONDS.toMillis(6); private long failTimeout = TimeUnit.SECONDS.toMillis(120); private Map<String, MonitorFactory> completedRequests = new HashMap<String, MonitorFactory>(); private long completedRequestsWaitTime = 500; protected final Map<String, MonitorFactory> getCompletedRequests() { return this.completedRequests; } public HttpServletResponseMonitorFactory handleRequest(final TimeoutProtectionHttpRequest request) { return new MonitorFactory(); } public void afterRequest(TimeoutProtectionHttpRequest request, HttpServletResponseMonitorFactory monitorFactory) { afterRequest(request, (MonitorFactory) monitorFactory); } private void afterRequest(TimeoutProtectionHttpRequest request, MonitorFactory monitorFactory) { if (monitorFactory.wasMonitored()) { synchronized (this.completedRequests) { purgeUnpolledRequests(); this.completedRequests.put(request.getUid(), monitorFactory); this.completedRequests.notifyAll(); } } } /** * Cleanup any started monitors that may have never received a poll. This can happen if the client is closed after a * timeout but before a poll. */ private void purgeUnpolledRequests() { Iterator<Map.Entry<String, MonitorFactory>> iterator = this.completedRequests.entrySet().iterator(); while (iterator.hasNext()) { if (iterator.next().getValue().isPurgable(this.threshold + this.failTimeout)) { iterator.remove(); } } } public void handlePoll(TimeoutProtectionHttpRequest request, HttpServletResponse response) throws IOException { String uid = request.getUid(); long startTime = System.currentTimeMillis(); do { MonitorFactory completedRequest; synchronized (this.completedRequests) { if (!this.completedRequests.containsKey(uid)) { try { this.completedRequests.wait(this.completedRequestsWaitTime); } catch (InterruptedException e) { } } completedRequest = this.completedRequests.remove(uid); } if (completedRequest != null) { completedRequest.replay(response); return; } } while (System.currentTimeMillis() - startTime < this.longPollTime); response.setHeader(TimeoutProtectionHttpHeader.POLL, uid); response.setStatus(HttpStatus.NO_CONTENT.value()); } @Override public long getThreshold() { return this.threshold; } /** * Set the threshold that must be passed before timeout protection will be used * @param threshold the threshold in milliseconds */ public void setThreshold(long threshold) { this.threshold = threshold; } /** * Set the maximum amount of time that a single long poll request can take. * @param longPollTime the long poll time in milliseconds */ public void setLongPollTime(long longPollTime) { this.longPollTime = longPollTime; } @Override public long getFailTimeout() { return this.failTimeout; } /** * Set the amount of time before a request is considered failed. * @param failTimeout the fail timeout in milliseconds */ public void setFailTimeout(long failTimeout) { this.failTimeout = failTimeout; } /** * Sets the amount of time to wait for a completed request. * @param completedRequestsWaitTime the amount of time to wait for completed requests */ protected final void setCompletedRequestsWaitTime(long completedRequestsWaitTime) { this.completedRequestsWaitTime = completedRequestsWaitTime; } /** * The {@link HttpServletResponseMonitorFactory} used internally. */ private class MonitorFactory implements HttpServletResponseMonitorFactory { private long startTime; private ReplayableHttpServletResponseMonitor monitor; public MonitorFactory() { this.startTime = System.currentTimeMillis(); } public boolean wasMonitored() { return this.monitor != null; } public void replay(HttpServletResponse response) throws IOException { Assert.state(wasMonitored(), "Request was not monitored, no poll expected"); this.monitor.getReplayableResponse().replay(response); } public HttpServletResponseMonitor getMonitor() { long pollThreshold = ReplayingTimeoutProtectionStrategy.this.threshold; if ((pollThreshold == 0) || (System.currentTimeMillis() - this.startTime >= pollThreshold)) { this.monitor = new ReplayableHttpServletResponseMonitorFactory().getMonitor(); } return this.monitor; } public boolean isPurgable(long timeout) { return (System.currentTimeMillis() - this.startTime > timeout); } } }