/**
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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.jboss.pnc.rest.notifications.websockets;

import org.jboss.pnc.spi.notifications.AttachedClient;
import org.jboss.pnc.spi.notifications.MessageCallback;
import org.jboss.pnc.spi.notifications.Notifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import java.lang.invoke.MethodHandles;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Notification mechanism for Web Sockets. All implementation details should be placed in AttachedClient.
 */
@ApplicationScoped
public class DefaultNotifier implements Notifier {

    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    private Set<AttachedClient> attachedClients = Collections.synchronizedSet(new HashSet<>());

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private final MessageCallback messageCallback = new MessageCallback() {

        @Override
        public void successful(AttachedClient attachedClient) {
            // logger.debug("Successfully sent message to client ", attachedClient);
        }

        @Override
        public void failed(AttachedClient attachedClient, Throwable throwable) {
            logger.error("Notification client threw an error, removing it", throwable);
            detachClient(attachedClient);
        }
    };

    @PostConstruct
    public void init() {
        scheduler.scheduleAtFixedRate(this::cleanUp, 1, 1, TimeUnit.HOURS);
    }

    @Override
    public void attachClient(AttachedClient attachedClient) {
        attachedClients.add(attachedClient);
    }

    @Override
    public void detachClient(AttachedClient attachedClient) {
        try {
            attachedClients.remove(attachedClient);
        } catch (ConcurrentModificationException cme) {
            logger.error("Error while removing attached client: ", cme);
        }
    }

    @Override
    public int getAttachedClientsCount() {
        return attachedClients.size();
    }

    @Override
    public MessageCallback getCallback() {
        return messageCallback;
    }

    @Override
    public void sendMessage(Object message) {
        try {
            for (Iterator<AttachedClient> attachedClientIterator = attachedClients.iterator(); attachedClientIterator
                    .hasNext();) {
                AttachedClient client = attachedClientIterator.next();
                if (client.isEnabled()) {
                    client.sendMessage(message, messageCallback);
                }
            }
        } catch (ConcurrentModificationException cme) {
            logger.error("Error while removing attached client: ", cme);
        }
    }

    public void cleanUp() {
        synchronized (attachedClients) {
            for (Iterator<AttachedClient> attachedClientIterator = attachedClients.iterator(); attachedClientIterator
                    .hasNext();) {
                AttachedClient client = attachedClientIterator.next();
                if (!client.isEnabled()) {
                    attachedClientIterator.remove();
                }
            }
        }
    }

}

@@ -3441,32 +3441,62 @@
 .isEnabled()) %7B%0A
+                    try %7B%0A    
                 
@@ -3545,16 +3545,214 @@
 lback);%0A
+                    %7D catch (Exception e) %7B%0A                        logger.error(%22Unable to send message, detaching client.%22, e);%0A                        detachClient(client);%0A                    %7D%0A
         

Handle WS client exception.