org.kitesdk.apps.streaming.StreamDescription.java Source code

Java tutorial

Introduction

Here is the source code for org.kitesdk.apps.streaming.StreamDescription.java

Source

/**
 * Copyright 2015 Cerner Corporation.
 *
 * 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.kitesdk.apps.streaming;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.kitesdk.apps.AppException;
import org.kitesdk.data.ValidationException;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.util.Iterator;
import java.util.Map;

/**
 * Description for a streaming job.
 */
public class StreamDescription {

    private final Class<? extends StreamingJob> jobClass;

    private final String jobName;

    private final Map<String, Stream> streams;

    private final Map<String, URI> viewUris;

    public Class<? extends StreamingJob> getJobClass() {
        return jobClass;
    }

    public String getJobName() {
        return jobName;
    }

    public Map<String, Stream> getStreams() {
        return streams;
    }

    public Map<String, URI> getViewUris() {
        return viewUris;
    }

    private StreamDescription(Class<? extends StreamingJob> jobClass, String jobName, Map<String, Stream> streams,
            Map<String, URI> viewUris) {

        Preconditions.checkNotNull(jobClass, "Job class must be provided.");
        Preconditions.checkNotNull(jobName, "Job name must be provided.");

        this.jobClass = jobClass;
        this.jobName = jobName;
        this.streams = streams;
        this.viewUris = viewUris;
    }

    public static class Stream {

        private final String name;

        private final Map<String, String> properties;

        Stream(String name, Map<String, String> properties) {
            this.name = name;
            this.properties = properties;
        }

        public String getName() {
            return name;
        }

        public Map<String, String> getProperties() {
            return properties;
        }

        public boolean equals(Object that) {

            if (this == that) {
                return true;
            }

            if (that == null || !(that instanceof Stream)) {
                return false;
            }

            Stream _that = (Stream) that;

            return name.equals(_that.name) && properties.equals(_that.properties);
        }

        public int hashCode() {

            return 37 * name.hashCode() * properties.hashCode();
        }
    }

    /**
     * Builder class for stream descriptions.
     */
    public static class Builder {

        private Class<? extends StreamingJob> jobClass;

        private String jobName;

        private final Map<String, Stream> streams = Maps.newHashMap();

        private final Map<String, URI> viewUris = Maps.newHashMap();

        public Builder jobName(String jobName) {
            this.jobName = jobName;

            return this;
        }

        /**
         * Sets the class of the {@link org.kitesdk.apps.streaming.StreamingJob}
         * being configured.
         *
         * @return An instance of the builder for method chaining.
         */
        public Builder jobClass(Class<? extends StreamingJob> jobClass) {

            this.jobClass = jobClass;

            return this;
        }

        /**
         * Defines a stream for use in the job.
         *
         * @param name
         * @param properties
         *
         * @return An instance of the builder for method chaining.
         */
        public Builder withStream(String name, Map<String, String> properties) {

            Stream stream = new Stream(name, properties);

            streams.put(name, stream);

            return this;

        }

        /**
         * Defines a view for use in the job.
         *
         * @param name
         * @param uri
         *
         * @return An instance of the builder for method chaining.
         */
        public Builder withView(String name, String uri) {

            viewUris.put(name, URI.create(uri));

            return this;
        }

        /**
         * Builds the description, returning an immutable
         * {@link org.kitesdk.apps.streaming.StreamDescription} instance.
         *
         * @return a StreamDescription.
         */
        public StreamDescription build() {
            return new StreamDescription(jobClass, jobName, streams, viewUris);
        }
    }

    private static String STREAMS = "streams";
    private static String VIEWS = "views";

    private static String JOBCLASS = "jobclass";
    private static String NAME = "name";
    private static String PROPS = "props";
    private static String URI_PROP = "uri";

    public static StreamDescription parseJson(String json) {

        ObjectMapper mapper = new ObjectMapper();

        JsonNode parent;

        try {
            parent = mapper.readValue(json, JsonNode.class);
        } catch (JsonParseException e) {
            throw new ValidationException("Invalid JSON", e);
        } catch (JsonMappingException e) {
            throw new ValidationException("Invalid JSON", e);
        } catch (IOException e) {
            throw new AppException(e);
        }

        StreamDescription.Builder builder = new StreamDescription.Builder();

        String jobName = parent.get(NAME).asText();

        builder.jobName(jobName);

        String className = parent.get(JOBCLASS).asText();

        try {
            Class jobClass = Thread.currentThread().getContextClassLoader().loadClass(className);

            builder.jobClass(jobClass);

        } catch (ClassNotFoundException e) {
            throw new AppException(e);
        }

        // Read the streams.
        ArrayNode streams = (ArrayNode) parent.get(STREAMS);

        for (JsonNode stream : streams) {

            String name = stream.get(NAME).asText();

            ObjectNode props = (ObjectNode) stream.get(PROPS);

            Map<String, String> properties = Maps.newHashMap();

            for (Iterator<Map.Entry<String, JsonNode>> it = props.fields(); it.hasNext();) {

                Map.Entry<String, JsonNode> property = it.next();

                properties.put(property.getKey(), property.getValue().asText());
            }

            builder.withStream(name, properties);
        }

        // Read the views.
        ArrayNode views = (ArrayNode) parent.get(VIEWS);

        for (JsonNode view : views) {

            String name = view.get(NAME).asText();
            String uri = view.get(URI_PROP).asText();

            builder.withView(name, uri);
        }

        return builder.build();
    }

    private TreeNode toJson() {

        JsonNodeFactory js = JsonNodeFactory.instance;

        ObjectNode root = js.objectNode();

        root.put(NAME, getJobName());
        root.put(JOBCLASS, getJobClass().getName());

        ArrayNode streamsArray = js.arrayNode();

        for (Stream stream : getStreams().values()) {

            ObjectNode streamNode = streamsArray.addObject();

            streamNode.put(NAME, stream.getName());

            ObjectNode properties = js.objectNode();

            for (Map.Entry<String, String> prop : stream.getProperties().entrySet()) {

                properties.put(prop.getKey(), prop.getValue());
            }

            streamNode.put(PROPS, properties);
        }

        root.put(STREAMS, streamsArray);

        ArrayNode viewsArray = js.arrayNode();

        for (Map.Entry<String, URI> view : getViewUris().entrySet()) {

            ObjectNode viewNode = viewsArray.addObject();

            viewNode.put(NAME, view.getKey());
            viewNode.put(URI_PROP, view.getValue().toString());
        }

        root.put(VIEWS, viewsArray);

        return root;
    }

    public String toString() {

        StringWriter writer = new StringWriter();

        try {

            JsonGenerator gen = new JsonFactory().createGenerator(writer);
            gen.setCodec(new ObjectMapper());
            gen.writeTree(toJson());

            gen.close();
        } catch (IOException e) {
            // An IOException should not be possible against a local buffer.
            throw new AssertionError(e);
        }

        return writer.toString();
    }

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

        if (that == null || !(that instanceof StreamDescription)) {
            return false;
        }

        StreamDescription _that = (StreamDescription) that;

        return jobName.equals(_that.jobName) && jobClass.equals(_that.jobClass) && streams.equals(_that.streams)
                && viewUris.equals(_that.viewUris);
    }

    @Override
    public int hashCode() {

        return 37 * jobClass.getName().hashCode() * streams.hashCode() * viewUris.hashCode();

    }
}