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

org.wildfly.discovery.Discovery Maven / Gradle / Ivy

/*
 * 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.wildfly.discovery;

import java.net.URI;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.jboss.logging.Logger;
import org.wildfly.common.Assert;
import org.wildfly.common.annotation.NotNull;
import org.wildfly.common.context.ContextManager;
import org.wildfly.common.context.Contextual;
import org.wildfly.discovery.impl.AggregateDiscoveryProvider;
import org.wildfly.discovery.spi.DiscoveryProvider;
import org.wildfly.discovery.spi.DiscoveryRequest;
import org.wildfly.discovery.spi.DiscoveryResult;

/**
 * The service discovery API.  Each discovery instance is associated with discovery providers which are able to
 * provide answers to discovery queries.
 *
 * @author David M. Lloyd
 */
public final class Discovery implements Contextual {

    private static final Logger log = Logger.getLogger("org.wildfly.discovery");
    private static final ServiceURL END_MARK = new ServiceURL.Builder().setUri(URI.create("DUMMY:DUMMY")).create();

    private static final ContextManager CONTEXT_MANAGER;

    static {
        CONTEXT_MANAGER = new ContextManager(Discovery.class, "org.wildfly.discovery");
        CONTEXT_MANAGER.setGlobalDefaultSupplier(() -> create(ConfiguredProvider.INSTANCE));
    }

    private final DiscoveryProvider provider;

    Discovery(final DiscoveryProvider provider) {
        this.provider = provider;
    }

    /**
     * Get the instance context manager.  Delegates to {@link #getContextManager()}.
     *
     * @return the instance context manager (not {@code null})
     */
    public ContextManager getInstanceContextManager() {
        return CONTEXT_MANAGER;
    }

    /**
     * Get the context manager.
     *
     * @return the context manager (not {@code null})
     */
    public static ContextManager getContextManager() {
        return CONTEXT_MANAGER;
    }

    /**
     * Perform a service discovery.  The returned services queue is populated as discovery answers become available.
     * Answers may be cached within each provider.  The order of answers is not significant and can vary from call to
     * call, especially with asynchronous discovery mechanisms.  The returned service queue may be closed to indicate
     * no further interest in query answers, and for this purpose it implements {@link AutoCloseable} in order to
     * facilitate simple usage in a {@code try}-with-resources block.
     *
     * @param serviceType the abstract or concrete type of service to search for
     * @param filterSpec the service filter specification
     * @return the services queue
     */
    public ServicesQueue discover(ServiceType serviceType, FilterSpec filterSpec) {
        Assert.checkNotNullParam("serviceType", serviceType);
        final LinkedBlockingQueue queue = new LinkedBlockingQueue<>();
        final CopyOnWriteArrayList problems = new CopyOnWriteArrayList<>();
        final DiscoveryResult result = new BlockingQueueDiscoveryResult(queue, problems);

        log.tracef("Calling discover(%s, %s) with result instance %s\n", serviceType, filterSpec, result);

        return new BlockingQueueServicesQueue(queue, problems, provider.discover(serviceType, filterSpec, result));
    }

    /**
     * Create a discovery object with the given providers.  The given {@code providers} argument and its array
     * elements may not be {@code null}.
     *
     * @param providers the discovery providers (must not be {@code null})
     * @return the discovery object
     */
    public static Discovery create(DiscoveryProvider... providers) {
        Assert.checkNotNullParam("providers", providers);
        final DiscoveryProvider[] clone = providers.clone();
        final int length = clone.length;
        for (int i = 0; i < length; i++) {
            Assert.checkNotNullArrayParam("providers", i, clone[i]);
        }
        if (clone.length == 0) {
            return new Discovery(DiscoveryProvider.EMPTY);
        } else if (clone.length == 1) {
            return new Discovery(clone[0]);
        } else {
            return new Discovery(new AggregateDiscoveryProvider(clone));
        }
    }

    /**
     * Create a discovery object with the given single provider.  The given {@code provider} argument may not be {@code null}.
     *
     * @param provider the discovery provider (must not be {@code null})
     * @return the discovery object
     */
    public static Discovery create(DiscoveryProvider provider) {
        Assert.checkNotNullParam("provider", provider);
        return new Discovery(provider);
    }

    // Internal classes

    static final class BlockingQueueDiscoveryResult implements DiscoveryResult {
        private final AtomicBoolean done = new AtomicBoolean(false);
        private final BlockingQueue queue;
        private final CopyOnWriteArrayList problems;

        BlockingQueueDiscoveryResult(final BlockingQueue queue, final CopyOnWriteArrayList problems) {
            this.queue = queue;
            this.problems = problems;
        }

        public void complete() {
            if (done.compareAndSet(false, true)) {
                queue.add(END_MARK);

                log.tracef("Discovery complete on %s\n", this);
            }
        }

        public void reportProblem(final Throwable description) {
            Assert.checkNotNullParam("description", description);
            problems.add(description);
            log.tracef(description, "Reported problem on %s", this);
        }

        public void addMatch(final ServiceURL serviceURL) {
            if (serviceURL != null && ! done.get()) {
                log.tracef("Adding service URL match \"%s\" to %s", serviceURL, this);
                // if the queue is full, drop
                queue.offer(serviceURL);
            } else {
                log.tracef("Ignoring service URL match \"%s\" to %s", serviceURL, this);
            }
        }
    }

    static final class BlockingQueueServicesQueue implements ServicesQueue {
        private final LinkedBlockingQueue queue;
        private final CopyOnWriteArrayList problems;
        private final DiscoveryRequest request;
        private ServiceURL next;
        private boolean done;

        BlockingQueueServicesQueue(final LinkedBlockingQueue queue, final CopyOnWriteArrayList problems, final DiscoveryRequest request) {
            this.queue = queue;
            this.problems = problems;
            this.request = request;
        }

        public void await() throws InterruptedException {
            if (done) return;
            while (next == null) {
                next = queue.take();
                if (next == END_MARK) {
                    next = null;
                    // sentinel value to indicate the provider completed
                    done = true;
                    return;
                }
            }
        }

        public void await(final long time, final TimeUnit unit) throws InterruptedException {
            long remaining = unit.toNanos(time);
            long mark = System.nanoTime();
            long now;
            while (next == null && ! done && remaining > 0L) {
                next = queue.poll(remaining, TimeUnit.NANOSECONDS);
                now = System.nanoTime();
                remaining -= Math.max(1L, now - mark);
                if (next == END_MARK) {
                    next = null;
                    // sentinel value to indicate the provider completed
                    done = true;
                    return;
                }
            }
        }

        public boolean isReady() {
            return next != null || done;
        }

        public ServiceURL pollService() {
            try {
                return next;
            } finally {
                next = null;
            }
        }

        public ServiceURL takeService() throws InterruptedException {
            await();
            return pollService();
        }

        public boolean isFinished() {
            return next == null && done;
        }

        public void close() {
            if (! isFinished()) {
                request.cancel();
            }
        }

        @NotNull
        public List getProblems() {
            return problems;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy