All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hotels.styx.api.service.spi.Registry Maven / Gradle / Ivy

There is a newer version: 1.0.0.beta9
Show newest version
/*
  Copyright (C) 2013-2018 Expedia Inc.

  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 com.hotels.styx.api.service.spi;

import com.google.common.collect.Iterables;
import com.hotels.styx.api.Environment;
import com.hotels.styx.api.Identifiable;
import com.hotels.styx.api.configuration.Configuration;
import com.hotels.styx.api.configuration.ServiceFactory;

import java.util.EventListener;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

import static com.hotels.styx.api.service.spi.Registry.Outcome.FAILED;
import static com.hotels.styx.api.service.spi.Registry.Outcome.RELOADED;
import static com.hotels.styx.api.service.spi.Registry.Outcome.UNCHANGED;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;

/**
 * Registry for resources of type {@code T}.
 *
 * @param  the type of resource to store
 */
public interface Registry extends Supplier> {

    /**
     * Register a {@link Registry.ChangeListener} to be notified when registry changes.
     *
     * @param changeListener {@link Registry.ChangeListener} to register.
     */
    Registry addListener(ChangeListener changeListener);

    /**
     * Remove a {@link Registry.ChangeListener}.
     *
     * @param changeListener {@link Registry.ChangeListener} to remove.
     */
    Registry removeListener(ChangeListener changeListener);

    CompletableFuture reload();

    /**
     * Factory for creating a registry.
     *
     * @param 
     */
    interface Factory extends ServiceFactory> {
        Registry create(Environment environment, Configuration registryConfiguration);
    }

    /**
     * A base interface for notification of a change in the registry.
     *
     * @param  The type of configuration referred to by this ChangeListener
     */
    interface ChangeListener extends EventListener {
        void onChange(Changes changes);
    }

    /**
     * The set of changes between reloads.
     *
     * @param 
     */
    final class Changes {
        private final Iterable added;
        private final Iterable removed;
        private final Iterable updated;

        private Changes(Builder builder) {
            this.added = nullToEmpty(builder.added);
            this.removed = nullToEmpty(builder.removed);
            this.updated = nullToEmpty(builder.updated);
        }

        private static  Iterable nullToEmpty(Iterable iterable) {
            return iterable != null ? iterable : emptyList();
        }

        public Iterable added() {
            return added;
        }

        public Iterable removed() {
            return removed;
        }

        public Iterable updated() {
            return updated;
        }

        @Override
        public int hashCode() {
            return Objects.hash(added, removed, updated);
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null || getClass() != obj.getClass()) {
                return false;
            }
            Changes other = (Changes) obj;

            return equal(this.added, other.added)
                    && equal(this.removed, other.removed)
                    && equal(this.updated, other.updated);
        }

        private boolean equal(Iterable iterable1, Iterable iterable2) {
            return Iterables.size(iterable1) == Iterables.size(iterable2) && containsAll(iterable1, iterable2);
        }

        private boolean containsAll(Iterable iterable1, Iterable iterable2) {
            for (T item : iterable2) {
                if (!Iterables.contains(iterable1, item)) {
                    return false;
                }
            }

            return true;
        }

        @Override
        public String toString() {
            return "Changes{"
                    + "added="
                    + added
                    + ", removed="
                    + removed
                    + ", updated="
                    + updated
                    + '}';
        }

        public Iterable addedAndUpdated() {
            return Iterables.concat(added, updated);
        }

        public boolean isEmpty() {
            return Iterables.isEmpty(added) && Iterables.isEmpty(removed) && Iterables.isEmpty(updated);
        }

        public static class Builder {
            private Iterable added;
            private Iterable removed;
            private Iterable updated;

            public Builder added(T... added) {
                return added(asList(added));
            }

            public Builder added(Iterable added) {
                this.added = requireNonNull(added);
                return this;
            }

            public Builder removed(T... added) {
                return removed(asList(added));
            }

            public Builder removed(Iterable removed) {
                this.removed = requireNonNull(removed);
                return this;
            }

            public Builder updated(T... updated) {
                return updated(asList(updated));
            }

            public Builder updated(Iterable updated) {
                this.updated = requireNonNull(updated);
                return this;
            }

            public Changes build() {
                return new Changes<>(this);
            }
        }
    }

    /**
     * Registry reload outcome.
     */
    enum Outcome {
        RELOADED,
        UNCHANGED,
        FAILED
    }

    /**
     * Result of registry reload.
     */
    class ReloadResult {
        private final Outcome outcome;
        private final String message;
        private final Throwable cause;

        private ReloadResult(Outcome outcome, String message, Throwable cause) {
            this.outcome = outcome;
            this.message = message;
            this.cause = cause;
        }

        public static ReloadResult reloaded(String message) {
            return new ReloadResult(RELOADED, message, null);
        }

        public static ReloadResult failed(String message, Throwable cause) {
            return new ReloadResult(FAILED, message, cause);
        }

        public static ReloadResult unchanged(String message) {
            return new ReloadResult(UNCHANGED, message, null);
        }

        public Outcome outcome() {
            return outcome;
        }

        public String message() {
            return message;
        }

        public Optional cause() {
            return Optional.ofNullable(cause);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            ReloadResult that = (ReloadResult) o;
            return outcome == that.outcome && Objects.equals(message, that.message) && Objects.equals(cause, that.cause);
        }

        @Override
        public int hashCode() {
            return Objects.hash(outcome, message, cause);
        }

        @Override
        public String toString() {
            return "ReloadResult{"
                    + "outcome="
                    + outcome
                    + ", message='" + message + '\''
                    + ", cause='" + cause + "\'"
                    + '}';
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy