/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.io.rest.sse.internal.async;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.smarthome.io.rest.sse.internal.util.SseUtil;
import org.glassfish.jersey.media.sse.SseFeature;
import org.glassfish.jersey.servlet.spi.AsyncContextDelegate;
import org.glassfish.jersey.servlet.spi.AsyncContextDelegateProvider;
/**
* An {@link AsyncContextDelegateProvider} implementation that returns a
* blocking {@link AsyncContextDelegate}, which blocks while the connection is
* alive if the response content-type is {@link SseFeature #SERVER_SENT_EVENTS} or throws an
* UnsupportedOperationException otherwise. The blocking continues
* until the response can longer be written to.
*
* @author Ivan Iliev - Initial Contribution and API
*
*/
public class BlockingAsyncContextDelegateProvider implements AsyncContextDelegateProvider {
@Override
public final AsyncContextDelegate createDelegate(final HttpServletRequest request,
final HttpServletResponse response) {
return new BlockingAsyncContextDelegate(request, response);
}
private static final class BlockingAsyncContextDelegate implements AsyncContextDelegate {
private static final int PING_TIMEOUT = 15 * 1000;
private final HttpServletResponse response;
private volatile boolean isRunning;
private BlockingAsyncContextDelegate(final HttpServletRequest request, final HttpServletResponse response) {
this.response = response;
}
@Override
public void complete() {
isRunning = false;
}
@Override
public void suspend() throws IllegalStateException {
if (SseUtil.shouldAsyncBlock()) {
isRunning = true;
synchronized (this) {
while (isRunning) {
try {
this.wait(PING_TIMEOUT);
ServletOutputStream outputStream = response.getOutputStream();
// write a new line to the OutputStream and flush to
// check connectivity. If the other peer closes the
// connection, the first flush() should generate a
// TCP reset that is detected on the second flush()
outputStream.write('\n');
response.flushBuffer();
outputStream.write('\n');
response.flushBuffer();
} catch (Exception exception) {
// If an exception has occurred during write and
// flush we consider the connection closed, attempt
// to close the outputstream and stop blocking.
try {
response.getOutputStream().close();
} catch (IOException e) {
}
isRunning = false;
}
}
}
} else {
throw new UnsupportedOperationException("ASYNCHRONOUS PROCESSING IS NOT SUPPORTED!");
}
}
}
}