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

com.couchbase.client.core.config.loader.AbstractLoader Maven / Gradle / Ivy

There is a newer version: 3.7.2
Show newest version
/**
 * Copyright (C) 2014 Couchbase, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
 * IN THE SOFTWARE.
 */
package com.couchbase.client.core.config.loader;

import com.couchbase.client.core.ClusterFacade;
import com.couchbase.client.core.config.BucketConfig;
import com.couchbase.client.core.config.LoaderType;
import com.couchbase.client.core.config.parser.BucketConfigParser;
import com.couchbase.client.core.env.CoreEnvironment;
import com.couchbase.client.core.lang.Tuple;
import com.couchbase.client.core.lang.Tuple2;
import com.couchbase.client.core.logging.CouchbaseLogger;
import com.couchbase.client.core.logging.CouchbaseLoggerFactory;
import com.couchbase.client.core.message.internal.AddNodeRequest;
import com.couchbase.client.core.message.internal.AddNodeResponse;
import com.couchbase.client.core.message.internal.AddServiceRequest;
import com.couchbase.client.core.message.internal.AddServiceResponse;
import com.couchbase.client.core.service.ServiceType;
import rx.Observable;
import rx.functions.Func1;
import java.net.InetAddress;
import java.util.Set;

/**
 * An {@link AbstractLoader} which provides common basic processing for all implementing loaders.
 *
 * A loader fetches configuration from a service, maybe falls back to another service and finally response with a
 * {@link BucketConfig} or an error. There are multiple steps, like making sure that a node or service is alive before
 * sending a request into, is abstracted in here to avoid duplication.
 *
 * @author Michael Nitschinger
 * @since 1.0
 */
public abstract class AbstractLoader implements Loader {

    /**
     * The logger used.
     */
    private static final CouchbaseLogger LOGGER = CouchbaseLoggerFactory.getInstance(Loader.class);

    /**
     * The reference to the cluster.
     */
    private final ClusterFacade cluster;

    /**
     * The couchbase environment.
     */
    private final CoreEnvironment environment;

    /**
     * The service serviceType from the actual loader implementation.
     */
    private final ServiceType serviceType;

    private final LoaderType loaderType;

    /**
     * Create a new {@link AbstractLoader}.
     *
     * @param serviceType the service serviceType.
     * @param cluster the cluster reference.
     * @param environment the couchbase environment.
     */
    protected AbstractLoader(final LoaderType loaderType, final ServiceType serviceType, final ClusterFacade cluster,
        final CoreEnvironment environment) {
        this.loaderType = loaderType;
        this.serviceType = serviceType;
        this.cluster = cluster;
        this.environment = environment;
    }

    /**
     * Port to use for the {@link ServiceType}.
     *
     * This method needs to be implemented by the actual loader and defines the port which should be used to
     * connect the service to. In practice, the actual port may depend on the environment (i.e. if SSL is used or not).
     *
     * @return the port for the service to enable.
     */
    protected abstract int port();

    /**
     * Run the {@link BucketConfig} discovery process.
     *
     * @param bucket the name of the bucket.
     * @param password the password of the bucket.
     * @param hostname the hostname of the seed node list.
     * @return a raw config if discovered.
     */
    protected abstract Observable discoverConfig(String bucket, String password, InetAddress hostname);

    /**
     * Initiate the config loading process.
     *
     *
     * @param seedNodes the seed nodes.
     * @param bucket the name of the bucket.
     * @param password the password of the bucket.
     * @return a valid {@link BucketConfig}.
     */
    public Observable> loadConfig(final Set seedNodes,
        final String bucket, final String password) {
        LOGGER.debug("Loading Config for bucket {}", bucket);

        return Observable.mergeDelayError(Observable
                .from(seedNodes)
                .map(new Func1>>() {
                    @Override
                    public Observable> call(InetAddress inetAddress) {
                        return loadConfigAtAddr(inetAddress, bucket, password);
                    }
                })
            )
            .take(1);
    }

    /**
     * Helper method to load a config at a specific {@link InetAddress}.
     *
     * The common path handled by this abstract implementation includes making sure that the node and service are
     * usable by the actual implementation. Finally, the raw config string config parsing is handled in this central
     * place.
     *
     * @param node the node to grab a config from.
     * @param bucket the name of the bucket.
     * @param password the password of the bucket.
     * @return a valid {@link BucketConfig} or an errored {@link Observable}.
     */
    private Observable> loadConfigAtAddr(final InetAddress node, final String bucket,
        final String password) {
        return Observable
            .just(node)
            .flatMap(new Func1>() {
                @Override
                public Observable call(final InetAddress address) {
                    return cluster.send(new AddNodeRequest(address));
                }
            }).flatMap(new Func1>() {
                @Override
                public Observable call(final AddNodeResponse response) {
                    if (!response.status().isSuccess()) {
                        return Observable.error(new IllegalStateException("Could not add node for config loading."));
                    }
                    LOGGER.debug("Successfully added Node {}", response.hostname());
                    return cluster.send(
                        new AddServiceRequest(serviceType, bucket, password, port(), response.hostname())
                    );
                }
            }).flatMap(new Func1>() {
                @Override
                public Observable call(final AddServiceResponse response) {
                    if (!response.status().isSuccess()) {
                        return Observable.error(new IllegalStateException("Could not add service for config loading."));
                    }
                    LOGGER.debug("Successfully enabled Service {} on Node {}", serviceType, response.hostname());
                    return discoverConfig(bucket, password, response.hostname());
                }
            })
            .map(new Func1>() {
                @Override
                public Tuple2 call(final String rawConfig) {
                    LOGGER.debug("Got configuration from Service, attempting to parse.");
                    BucketConfig config = BucketConfigParser.parse(rawConfig);
                    config.password(password);
                    return Tuple.create(loaderType, config);
                }
            });
    }

    /**
     * Returns the {@link ClusterFacade} for child implementations.
     *
     * @return the cluster reference.
     */
    protected ClusterFacade cluster() {
        return cluster;
    }

    /**
     * Returns the {@link CoreEnvironment} for child implementations.
     *
     * @return the environment.
     */
    protected CoreEnvironment env() {
        return environment;
    }

    /**
     * Replaces the host wildcard from an incoming config with a proper hostname.
     *
     * @param input the input config.
     * @param hostname the hostname to replace it with.
     * @return a replaced configuration.
     */
    protected String replaceHostWildcard(String input, InetAddress hostname) {
        return input.replace("$HOST", hostname.getHostName());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy