/*
 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * 
 * $Id: ServiceUnavailableResource.java 74997 2017-05-26 11:54:50Z jsupol $  
 */

package com.sun.ts.tests.jaxrs.jaxrs21.ee.sse.sseeventsource;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.sse.OutboundSseEvent;
import javax.ws.rs.sse.Sse;
import javax.ws.rs.sse.SseEventSink;

import com.sun.ts.tests.jaxrs.jaxrs21.ee.sse.OutboundSSEEventImpl;
import com.sun.ts.tests.jaxrs.jaxrs21.ee.sse.SSEMessage;

@Path("su")
public class ServiceUnavailableResource {
	private static volatile Boolean isServiceUnavailable = false;
	private static volatile int isConnectionLost = 0;
	private static volatile int retry = 1;
	private static int count = 0;
	static final String MESSAGE = SSEMessage.MESSAGE;

	@GET
	@Path("reset")
	public String reset() {
		retry = 0;
		isServiceUnavailable = false;
		isConnectionLost = 0;
		count = 0;
		return "RESET";
	}
	
	@GET
	@Path("count")
	public int count() {
		return count;
	}

	@GET
	@Path("available")
	@Produces(MediaType.TEXT_PLAIN)
	public String setUnavailable() {
		synchronized (isServiceUnavailable) {
			isServiceUnavailable = true;
			retry = 1;
		}
		return "OK";
	}

	@POST
	@Path("lost")
	@Produces(MediaType.TEXT_PLAIN)
	public int setConnectionLost(int count) {
		synchronized (isServiceUnavailable) {
			isConnectionLost = count;
			isServiceUnavailable = false;
			retry = 1;
		}
		return count;
	}

	@POST
	@Path("retry")
	@Produces(MediaType.TEXT_PLAIN)
	public int retry(int seconds) {
		synchronized (isServiceUnavailable) {
			retry = seconds;
		}
		return seconds;
	}

	@GET
	@Path("sse")
	@Produces(MediaType.SERVER_SENT_EVENTS)
	public void sendMessage(@Context SseEventSink sink, @Context Sse sse) {
		synchronized (isServiceUnavailable) {
			if (isServiceUnavailable) {
				isServiceUnavailable = false;
				throw new WebApplicationException(
						Response.status(503).header(HttpHeaders.RETRY_AFTER, String.valueOf(retry)).build());
			} else {
				try (SseEventSink s = sink) {
					s.send(sse.newEvent(MESSAGE));
				}
			}
		}
	}

	@GET
	@Path("sselost")
	@Produces(MediaType.SERVER_SENT_EVENTS)
	public void sseLost(@Context SseEventSink sink, @Context Sse sse) {
		synchronized (isServiceUnavailable) {
			count ++;
			if (isConnectionLost != 0) {
				isConnectionLost--;
				sink.close();
				/*
				 * To cancel a stream from the server, respond with a non "text/event-stream" Content-Type or 
				 * return an HTTP status other than 200 OK and 503 Service Unavailable (e.g. 404 Not Found).
				 */
			} else {
				try (SseEventSink s = sink) {
					s.send(sse.newEvent(MESSAGE));
				}
			}
		}
	}

	@GET
	@Path("reconnectdelay")
	@Produces(MediaType.SERVER_SENT_EVENTS)
	public void sendRetry(@Context SseEventSink sink, @Context Sse sse) {
		try (SseEventSink s = sink) {
			s.send(sse.newEventBuilder().data(MESSAGE).reconnectDelay(3000L).build());
		}
	}

	@GET
	@Path("userreconnectdelay")
	@Produces(MediaType.SERVER_SENT_EVENTS)
	public void sendUserRetry(@Context SseEventSink sink, @Context Sse sse) {
		try (SseEventSink s = sink) {
			s.send((OutboundSseEvent) new OutboundSSEEventImpl(MESSAGE).setDelay(20000));
		}
	}
}
