org.smartdeveloperhub.harvesters.scm.backend.notification.NotificationConsumer.java Source code

Java tutorial

Introduction

Here is the source code for org.smartdeveloperhub.harvesters.scm.backend.notification.NotificationConsumer.java

Source

/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the Smart Developer Hub Project:
 *     http://www.smartdeveloperhub.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2015-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.smartdeveloperhub.harvesters.scm:scm-harvester-backend:0.3.0
 *   Bundle      : scm-harvester-backend-0.3.0.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.smartdeveloperhub.harvesters.scm.backend.notification;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.BlockingQueue;

import org.apache.http.HttpHeaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.ImmutableList;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

final class NotificationConsumer extends DefaultConsumer {

    private interface NotificationHandler {

        boolean canHandle(String routingKey);

        SuspendedNotification suspend(AcknowledgeableNotification notification, String payload) throws IOException;

    }

    protected abstract static class CustomSuspendedNotification<T extends Event> implements SuspendedNotification {

        private final AcknowledgeableNotification notification;
        private final T event;

        protected CustomSuspendedNotification(final AcknowledgeableNotification notification, final T event) {
            this.notification = notification;
            this.event = event;
        }

        protected final T getEvent() {
            return this.event;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void consume() {
            this.notification.consume();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void discard(final Throwable exception) {
            this.notification.discard(exception);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public final void resume(final NotificationListener listener) {
            try {
                doResume(listener);
            } finally {
                enforceAcknowledgement();
            }
        }

        private void enforceAcknowledgement() {
            if (!this.notification.isAcknowledged()) {
                this.notification.acknowledge();
            }
        }

        protected abstract void doResume(NotificationListener listener);

    }

    private abstract static class CustomNotificationHandler<T extends Event> implements NotificationHandler {

        private final String acceptedKey;
        private final Class<? extends T> clazz;

        protected CustomNotificationHandler(final Class<? extends T> clazz) {
            this.clazz = clazz;
            this.acceptedKey = Notifications.routingKey(clazz);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public final boolean canHandle(final String routingKey) {
            return this.acceptedKey.equals(routingKey);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public final SuspendedNotification suspend(final AcknowledgeableNotification notification,
                final String payload) throws IOException {
            return createPropagator(notification, EventUtil.unmarshall(payload, this.clazz));
        }

        protected abstract CustomSuspendedNotification<T> createPropagator(AcknowledgeableNotification notification,
                T event);

    }

    private static final class CommitterCreatedNotificationHandler
            extends CustomNotificationHandler<CommitterCreatedEvent> {

        protected CommitterCreatedNotificationHandler() {
            super(CommitterCreatedEvent.class);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected CustomSuspendedNotification<CommitterCreatedEvent> createPropagator(
                final AcknowledgeableNotification notification, final CommitterCreatedEvent event) {
            return new CustomSuspendedNotification<CommitterCreatedEvent>(notification, event) {
                @Override
                protected void doResume(final NotificationListener listener) {
                    listener.onCommitterCreation(this, super.getEvent());
                }
            };
        }

    }

    private static final class CommitterDeletedNotificationHandler
            extends CustomNotificationHandler<CommitterDeletedEvent> {

        protected CommitterDeletedNotificationHandler() {
            super(CommitterDeletedEvent.class);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected CustomSuspendedNotification<CommitterDeletedEvent> createPropagator(
                final AcknowledgeableNotification notification, final CommitterDeletedEvent event) {
            return new CustomSuspendedNotification<CommitterDeletedEvent>(notification, event) {
                @Override
                protected void doResume(final NotificationListener listener) {
                    listener.onCommitterDeletion(this, super.getEvent());
                }
            };
        }

    }

    private static final class RepositoryCreatedNotificationHandler
            extends CustomNotificationHandler<RepositoryCreatedEvent> {

        protected RepositoryCreatedNotificationHandler() {
            super(RepositoryCreatedEvent.class);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected CustomSuspendedNotification<RepositoryCreatedEvent> createPropagator(
                final AcknowledgeableNotification notification, final RepositoryCreatedEvent event) {
            return new CustomSuspendedNotification<RepositoryCreatedEvent>(notification, event) {
                @Override
                protected void doResume(final NotificationListener listener) {
                    listener.onRepositoryCreation(this, super.getEvent());
                }
            };
        }
    }

    private static final class RepositoryDeletedNotificationHandler
            extends CustomNotificationHandler<RepositoryDeletedEvent> {

        protected RepositoryDeletedNotificationHandler() {
            super(RepositoryDeletedEvent.class);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected CustomSuspendedNotification<RepositoryDeletedEvent> createPropagator(
                final AcknowledgeableNotification notification, final RepositoryDeletedEvent event) {
            return new CustomSuspendedNotification<RepositoryDeletedEvent>(notification, event) {
                @Override
                protected void doResume(final NotificationListener listener) {
                    listener.onRepositoryDeletion(this, super.getEvent());
                }
            };
        }
    }

    private static final class RepositoryUpdatedNotificationHandler
            extends CustomNotificationHandler<RepositoryUpdatedEvent> {

        protected RepositoryUpdatedNotificationHandler() {
            super(RepositoryUpdatedEvent.class);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected CustomSuspendedNotification<RepositoryUpdatedEvent> createPropagator(
                final AcknowledgeableNotification notification, final RepositoryUpdatedEvent event) {
            return new CustomSuspendedNotification<RepositoryUpdatedEvent>(notification, event) {
                @Override
                protected void doResume(final NotificationListener listener) {
                    listener.onRepositoryUpdate(this, super.getEvent());
                }
            };
        }

    }

    private static final Logger LOGGER = LoggerFactory.getLogger(NotificationConsumer.class);

    private final BlockingQueue<SuspendedNotification> notifications;
    private final List<NotificationHandler> handlers;

    NotificationConsumer(final Channel channel, final BlockingQueue<SuspendedNotification> pendingNotifications) {
        super(channel);
        this.notifications = pendingNotifications;
        this.handlers = ImmutableList.<NotificationHandler>builder().add(new CommitterCreatedNotificationHandler())
                .add(new CommitterDeletedNotificationHandler()).add(new RepositoryCreatedNotificationHandler())
                .add(new RepositoryDeletedNotificationHandler()).add(new RepositoryUpdatedNotificationHandler())
                .build();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void handleDelivery(final String consumerTag, final Envelope envelope, final BasicProperties properties,
            final byte[] body) throws IOException {
        final String payload = new String(body, "UTF-8");
        final String routingKey = envelope.getRoutingKey();
        final AcknowledgeableNotification notification = new AcknowledgeableNotification(super.getChannel(),
                envelope.getDeliveryTag());
        try {
            verifyHeader(properties);
            this.notifications.offer(findHandler(routingKey).suspend(notification, payload));
        } catch (final Exception e) {
            LOGGER.error("Discarding message:\n{}\nReason:\n", payload, e);
            notification.acknowledge();
        }
    }

    private NotificationHandler findHandler(final String routingKey) {
        for (final NotificationHandler handler : this.handlers) {
            if (handler.canHandle(routingKey)) {
                return handler;
            }
        }
        throw new IllegalStateException("Unsupported routing key " + routingKey);
    }

    private void verifyHeader(final BasicProperties properties) throws IOException {
        final Object header = properties.getHeaders().get(HttpHeaders.CONTENT_TYPE);
        checkNotNull(header, "No %s header defined", HttpHeaders.CONTENT_TYPE);
        checkArgument(Notifications.MIME.equals(header.toString()), "Unsupported %s header (%s)",
                HttpHeaders.CONTENT_TYPE, header);
    }

}