/*
 * Copyright 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 de.codecentric.boot.admin.journal.web;

import static java.util.Collections.synchronizedCollection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.http.MediaType;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import de.codecentric.boot.admin.event.ClientApplicationEvent;
import de.codecentric.boot.admin.journal.ApplicationEventJournal;
import de.codecentric.boot.admin.web.AdminController;

/**
 * REST-Controller for querying all client application events.
 *
 * @author Johannes Edmeier
 */
@AdminController
@ResponseBody
@RequestMapping("/api/journal")
public class JournalController {
	private static final Logger LOGGER = LoggerFactory.getLogger(JournalController.class);

	private ApplicationEventJournal eventJournal;
	private final Collection<SseEmitter> emitters = synchronizedCollection(
			new LinkedList<SseEmitter>());

	public JournalController(ApplicationEventJournal eventJournal) {
		this.eventJournal = eventJournal;
	}

	@RequestMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
	public Collection<ClientApplicationEvent> getJournal() {
		return eventJournal.getEvents();
	}

	@RequestMapping(produces = "text/event-stream")
	public SseEmitter getJournalEvents() {
		final SseEmitter emitter = new SseEmitter();
		emitter.onCompletion(new Runnable() {
			@Override
			public void run() {
				emitters.remove(emitter);
			}
		});
		emitters.add(emitter);
		return emitter;
	}

	@EventListener
	public void onClientApplicationEvent(ClientApplicationEvent event) {
		for (SseEmitter emitter : new ArrayList<>(emitters)) {
			try {
				emitter.send(event, MediaType.APPLICATION_JSON);
			} catch (Exception ex) {
				LOGGER.debug("Error sending event to client ", ex);
			}
		}
	}
}

/*
 * Copyright 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 de.codecentric.boot.admin.journal.web;

import static java.util.Collections.synchronizedCollection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.http.MediaType;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import de.codecentric.boot.admin.event.ClientApplicationEvent;
import de.codecentric.boot.admin.journal.ApplicationEventJournal;
import de.codecentric.boot.admin.web.AdminController;

/**
 * REST-Controller for querying all client application events.
 *
 * @author Johannes Edmeier
 */
@AdminController
@ResponseBody
@RequestMapping("/api/journal")
public class JournalController {
	private static final Logger LOGGER = LoggerFactory.getLogger(JournalController.class);

	private ApplicationEventJournal eventJournal;
	private final Collection<SseEmitter> emitters = synchronizedCollection(
			new LinkedList<SseEmitter>());

	public JournalController(ApplicationEventJournal eventJournal) {
		this.eventJournal = eventJournal;
	}

	@RequestMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
	public Collection<ClientApplicationEvent> getJournal() {
		return eventJournal.getEvents();
	}

	@RequestMapping(produces = "text/event-stream")
	public SseEmitter getJournalEvents() {
		final SseEmitter emitter = new SseEmitter();
		emitter.onCompletion(new Runnable() {
			@Override
			public void run() {
				emitters.remove(emitter);
			}
		});
		emitters.add(emitter);
		return emitter;
	}

	@EventListener
	public void onClientApplicationEvent(ClientApplicationEvent event) {
		for (SseEmitter emitter : new ArrayList<>(emitters)) {
			try {
				emitter.send(event, MediaType.APPLICATION_JSON);
			} catch (Exception ex) {
				LOGGER.debug("Error sending event to client ", ex);
			}
		}
	}

	@ExceptionHandler(AsyncRequestTimeoutException.class)
	public void asyncRequestTimeoutExceptionHandler(HttpServletRequest req) {
		LOGGER.debug("Async request to '{}' timed out", req.getRequestURI());
	}
}

Add Handler for AsyncRequestTimeoutException