gobblin.runtime.api.TopologySpec.java Source code

Java tutorial

Introduction

Here is the source code for gobblin.runtime.api.TopologySpec.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 gobblin.runtime.api;

import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Properties;
import javax.annotation.concurrent.NotThreadSafe;

import edu.umd.cs.findbugs.annotations.SuppressWarnings;

import lombok.AllArgsConstructor;
import lombok.Data;

import org.apache.commons.lang3.reflect.ConstructorUtils;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

import gobblin.annotation.Alpha;
import gobblin.configuration.ConfigurationKeys;
import gobblin.runtime.spec_executorInstance.InMemorySpecExecutorInstanceProducer;
import gobblin.util.ClassAliasResolver;
import gobblin.util.ConfigUtils;

/**
 * Data model representation that describes a topology ie. a {@link SpecExecutorInstance} and its
 * capabilities tuple .
 *
 */
@Alpha
@Data
@AllArgsConstructor
@NotThreadSafe
public class TopologySpec implements Configurable, Spec {
    public static final String DEFAULT_SPEC_EXECUTOR_INSTANCE_PRODUCER = InMemorySpecExecutorInstanceProducer.class
            .getCanonicalName();
    public static final String SPEC_EXECUTOR_INSTANCE_PRODUCER_KEY = "specExecutorInstanceProducer.class";

    private static final long serialVersionUID = 6106269076155338046L;

    /** An URI identifying the topology. */
    final URI uri;

    /** The implementation-defined version of this spec. */
    final String version;

    /** Human-readable description of the topology spec */
    final String description;

    /** Topology config as a typesafe config object*/
    @SuppressWarnings(justification = "No bug", value = "SE_BAD_FIELD")
    final Config config;

    /** Topology config as a properties collection for backwards compatibility */
    // Note that this property is not strictly necessary as it can be generated from the typesafe
    // config. We use it as a cache until typesafe config is more widely adopted in Gobblin.
    final Properties configAsProperties;

    /** Underlying executor instance such as Gobblin cluster or Azkaban */
    @SuppressWarnings(justification = "Initialization handled by getter", value = "SE_TRANSIENT_FIELD_NOT_RESTORED")
    transient SpecExecutorInstanceProducer specExecutorInstanceProducer;

    public SpecExecutorInstanceProducer getSpecExecutorInstanceProducer() {
        if (null == specExecutorInstanceProducer) {
            String specExecutorInstanceProducerClass = DEFAULT_SPEC_EXECUTOR_INSTANCE_PRODUCER;
            if (config.hasPath(SPEC_EXECUTOR_INSTANCE_PRODUCER_KEY)) {
                specExecutorInstanceProducerClass = config.getString(SPEC_EXECUTOR_INSTANCE_PRODUCER_KEY);
            }
            try {
                ClassAliasResolver<SpecExecutorInstanceProducer> _aliasResolver = new ClassAliasResolver<>(
                        SpecExecutorInstanceProducer.class);
                specExecutorInstanceProducer = (SpecExecutorInstanceProducer) ConstructorUtils.invokeConstructor(
                        Class.forName(_aliasResolver.resolve(specExecutorInstanceProducerClass)), config);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException
                    | InstantiationException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        return specExecutorInstanceProducer;
    }

    public static TopologySpec.Builder builder(URI topologySpecUri) {
        return new TopologySpec.Builder(topologySpecUri);
    }

    public static TopologySpec.Builder builder(String topologySpecUri) {
        return new TopologySpec.Builder(topologySpecUri);
    }

    public static TopologySpec.Builder builder() {
        return new TopologySpec.Builder();
    }

    /** Creates a builder for the TopologySpec based on values in a topology properties config. */
    public static TopologySpec.Builder builder(URI catalogURI, Properties topologyProps) {
        String name = topologyProps.getProperty(ConfigurationKeys.TOPOLOGY_NAME_KEY);
        String group = topologyProps.getProperty(ConfigurationKeys.TOPOLOGY_GROUP_KEY, "default");

        try {
            URI topologyURI = new URI(catalogURI.getScheme(), catalogURI.getAuthority(), "/" + group + "/" + name,
                    null);
            TopologySpec.Builder builder = new TopologySpec.Builder(topologyURI)
                    .withConfigAsProperties(topologyProps);
            String descr = topologyProps.getProperty(ConfigurationKeys.TOPOLOGY_DESCRIPTION_KEY, null);
            if (null != descr) {
                builder = builder.withDescription(descr);
            }

            return builder;
        } catch (URISyntaxException e) {
            throw new RuntimeException("Unable to create a TopologySpec URI: " + e, e);
        }
    }

    public String toShortString() {
        return getUri().toString() + "/" + getVersion();
    }

    public String toLongString() {
        return getUri().toString() + "/" + getVersion() + "[" + getDescription() + "]";
    }

    @Override
    public String toString() {
        return toShortString();
    }

    /**
     * Builder for {@link TopologySpec}s.
     * <p> Defaults/conventions:
     * <ul>
     *  <li> Default topologyCatalogURI is {@link #DEFAULT_TOPOLOGY_CATALOG_SCHEME}:
     *  <li> Convention for TopologySpec URI: <topologyCatalogURI>/config.get({@link ConfigurationKeys#TOPOLOGY_GROUP_KEY})/config.get({@link ConfigurationKeys#TOPOLOGY_NAME_KEY})
     *  <li> Convention for Description: config.get({@link ConfigurationKeys#TOPOLOGY_DESCRIPTION_KEY})
     *  <li> Default version: 1
     * </ul>
     */
    public static class Builder {
        public static final String DEFAULT_TOPOLOGY_CATALOG_SCHEME = "gobblin-topology";
        @VisibleForTesting
        private Optional<Config> config = Optional.absent();
        private Optional<Properties> configAsProperties = Optional.absent();
        private Optional<URI> uri;
        private String version = "1";
        private Optional<String> description = Optional.absent();
        private Optional<URI> topologyCatalogURI = Optional.absent();
        private Optional<SpecExecutorInstanceProducer> specExecutorInstanceProducer = Optional.absent();

        public Builder(URI topologySpecUri) {
            Preconditions.checkNotNull(topologySpecUri);
            this.uri = Optional.of(topologySpecUri);
        }

        public Builder(String topologySpecUri) {
            Preconditions.checkNotNull(topologySpecUri);
            Preconditions.checkNotNull(topologySpecUri);
            try {
                this.uri = Optional.of(new URI(topologySpecUri));
            } catch (URISyntaxException e) {
                throw new RuntimeException("Invalid TopologySpec config: " + e, e);
            }
        }

        public Builder() {
            this.uri = Optional.absent();
        }

        public TopologySpec build() {
            Preconditions.checkNotNull(this.uri);
            Preconditions.checkNotNull(this.version);

            return new TopologySpec(getURI(), getVersion(), getDescription(), getConfig(), getConfigAsProperties(),
                    getSpecExceutorInstanceProducer());
        }

        /** The scheme and authority of the topology catalog URI are used to generate TopologySpec URIs from
         * topology configs. */
        public TopologySpec.Builder withTopologyCatalogURI(URI topologyCatalogURI) {
            this.topologyCatalogURI = Optional.of(topologyCatalogURI);
            return this;
        }

        public TopologySpec.Builder withTopologyCatalogURI(String topologyCatalogURI) {
            try {
                this.topologyCatalogURI = Optional.of(new URI(topologyCatalogURI));
            } catch (URISyntaxException e) {
                throw new RuntimeException("Unable to set topology catalog URI: " + e, e);
            }
            return this;
        }

        public URI getDefaultTopologyCatalogURI() {
            try {
                return new URI(DEFAULT_TOPOLOGY_CATALOG_SCHEME, null, "/", null, null);
            } catch (URISyntaxException e) {
                // should not happen
                throw new Error("Unexpected exception: " + e, e);
            }
        }

        public URI getTopologyCatalogURI() {
            if (!this.topologyCatalogURI.isPresent()) {
                this.topologyCatalogURI = Optional.of(getDefaultTopologyCatalogURI());
            }
            return this.topologyCatalogURI.get();
        }

        public URI getDefaultURI() {
            URI topologyCatalogURI = getTopologyCatalogURI();
            Config topologyCfg = getConfig();
            String name = topologyCfg.hasPath(ConfigurationKeys.TOPOLOGY_NAME_KEY)
                    ? topologyCfg.getString(ConfigurationKeys.TOPOLOGY_NAME_KEY)
                    : "default";
            String group = topologyCfg.hasPath(ConfigurationKeys.TOPOLOGY_GROUP_KEY)
                    ? topologyCfg.getString(ConfigurationKeys.TOPOLOGY_GROUP_KEY)
                    : "default";
            try {
                return new URI(topologyCatalogURI.getScheme(), topologyCatalogURI.getAuthority(),
                        "/" + group + "/" + name, null, null);
            } catch (URISyntaxException e) {
                throw new RuntimeException("Unable to create default TopologySpec URI:" + e, e);
            }
        }

        public URI getURI() {
            if (!this.uri.isPresent()) {
                this.uri = Optional.of(getDefaultURI());
            }

            return this.uri.get();
        }

        public TopologySpec.Builder withVersion(String version) {
            Preconditions.checkNotNull(version);
            this.version = version;
            return this;
        }

        public String getVersion() {
            return this.version;
        }

        public TopologySpec.Builder withDescription(String topologyDescription) {
            Preconditions.checkNotNull(topologyDescription);
            this.description = Optional.of(topologyDescription);
            return this;
        }

        public String getDefaultDescription() {
            Config topologyConf = getConfig();
            return topologyConf.hasPath(ConfigurationKeys.TOPOLOGY_DESCRIPTION_KEY)
                    ? topologyConf.getString(ConfigurationKeys.TOPOLOGY_DESCRIPTION_KEY)
                    : "Gobblin topology " + getURI();
        }

        public String getDescription() {
            if (!this.description.isPresent()) {
                this.description = Optional.of(getDefaultDescription());
            }
            return this.description.get();
        }

        public Config getDefaultConfig() {
            return ConfigFactory.empty();
        }

        public Config getConfig() {
            if (!this.config.isPresent()) {
                this.config = this.configAsProperties.isPresent() ? Optional.of(ConfigUtils
                        .propertiesToTypedConfig(this.configAsProperties.get(), Optional.<String>absent()))
                        : Optional.of(getDefaultConfig());
            }
            return this.config.get();
        }

        public TopologySpec.Builder withConfig(Config topologyConfig) {
            Preconditions.checkNotNull(topologyConfig);
            this.config = Optional.of(topologyConfig);
            return this;
        }

        public Properties getConfigAsProperties() {
            if (!this.configAsProperties.isPresent()) {
                this.configAsProperties = Optional.of(ConfigUtils.configToProperties(this.config.get()));
            }
            return this.configAsProperties.get();
        }

        public TopologySpec.Builder withConfigAsProperties(Properties topologyConfig) {
            Preconditions.checkNotNull(topologyConfig);
            this.configAsProperties = Optional.of(topologyConfig);
            return this;
        }

        public SpecExecutorInstanceProducer getSpecExceutorInstanceProducer() {
            if (!this.specExecutorInstanceProducer.isPresent()) {
                // TODO: Try to init SpecExecutorInstanceProducer from config if not initialized via builder.
                throw new RuntimeException("SpecExecutorInstanceProducer not initialized.");
            }
            return this.specExecutorInstanceProducer.get();
        }

        public TopologySpec.Builder withSpecExecutorInstanceProducer(
                SpecExecutorInstanceProducer specExecutorInstanceProducer) {
            Preconditions.checkNotNull(specExecutorInstanceProducer);
            this.specExecutorInstanceProducer = Optional.of(specExecutorInstanceProducer);
            return this;
        }
    }

    /**
     * get the private uri as the primary key for this object.
     * @return
     */
    public URI getUri() {
        return this.uri;
    }

}