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

com.netflix.genie.common.internal.dtos.CommonMetadata Maven / Gradle / Ivy

The newest version!
/*
 *
 *  Copyright 2018 Netflix, 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.netflix.genie.common.internal.dtos;

import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.ImmutableSet;
import com.netflix.genie.common.external.util.GenieObjectMapper;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nullable;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.io.IOException;
import java.io.Serializable;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Metadata fields common to all Genie resources (Jobs, clusters, etc).
 *
 * @author tgianos
 * @since 4.0.0
 */
@Getter
@EqualsAndHashCode(doNotUseGetters = true)
@ToString(doNotUseGetters = true)
public abstract class CommonMetadata implements Serializable {

    private static final long serialVersionUID = 7789443514882247655L;

    @NotBlank(message = "A name is required and must be at most 255 characters")
    @Size(max = 255, message = "The name can be no longer than 255 characters")
    private final String name;
    @NotBlank(message = "A user is required and must be at most 255 characters")
    @Size(max = 255, message = "The user can be no longer than 255 characters")
    private final String user;
    @NotBlank(message = "A version is required and must be at most 255 characters")
    @Size(max = 255, message = "The version can be no longer than 255 characters")
    private final String version;
    @Size(max = 1000, message = "The description can be no longer than 1000 characters")
    private final String description;
    private final JsonNode metadata;
    private final ImmutableSet<
        @NotEmpty(message = "A tag can't be an empty string")
        @Size(max = 255, message = "A tag can't be longer than 255 characters") String> tags;

    /**
     * Constructor.
     *
     * @param builder The builder containing the values to use.
     */
    @SuppressWarnings("unchecked")
    protected CommonMetadata(final Builder builder) {
        this.name = builder.bName;
        this.user = builder.bUser;
        this.version = builder.bVersion;
        this.description = builder.bDescription;
        this.metadata = builder.bMetadata;
        this.tags = builder.bTags == null ? ImmutableSet.of() : ImmutableSet.copyOf(builder.bTags);
    }

    /**
     * Get the description.
     *
     * @return The description as an {@link Optional}
     */
    public Optional getDescription() {
        return Optional.ofNullable(this.description);
    }

    /**
     * Get the metadata of this resource as a JSON Node.
     *
     * @return {@link Optional} of the metadata if it exists
     */
    public Optional getMetadata() {
        return Optional.ofNullable(this.metadata);
    }

    /**
     * Get the tags associated with this resource. Will be returned as an immutable set and any attempt to modify will
     * result in an exception being thrown.
     *
     * @return The tags
     */
    public Set getTags() {
        return this.tags;
    }

    /**
     * Builder for common fields.
     *
     * @param  Type of builder that extends this
     * @author tgianos
     * @since 4.0.0
     */
    // NOTE: These abstract class builders are marked public not protected due to a JDK bug from 1999 which caused
    //       issues with Clojure clients which use reflection to make the Java API calls.
    //       http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4283544
    //       Setting them to public seems to have solved the issue at the expense of "proper" code design
    @SuppressWarnings("unchecked")
    public static class Builder {

        private final String bName;
        private final String bUser;
        private final String bVersion;
        private String bDescription;
        private JsonNode bMetadata;
        private ImmutableSet bTags;

        /**
         * Constructor with required fields.
         *
         * @param name    The name of the resource
         * @param user    The user owning the resource
         * @param version The version of hte resource
         */
        protected Builder(
            final String name,
            final String user,
            final String version
        ) {
            this.bName = name;
            this.bUser = user;
            this.bVersion = version;
        }

        /**
         * Set the description for the resource.
         *
         * @param description The description to use
         * @return The builder
         */
        public T withDescription(@Nullable final String description) {
            this.bDescription = StringUtils.isBlank(description) ? null : description;
            return (T) this;
        }

        /**
         * Set the tags to use for the resource.
         *
         * @param tags The tags to use. Blanks will be removed
         * @return The builder
         */
        public T withTags(@Nullable final Set tags) {
            this.bTags = tags == null ? ImmutableSet.of() : ImmutableSet.copyOf(
                tags
                    .stream()
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.toSet())
            );
            return (T) this;
        }

        /**
         * With the metadata to set for the job as a JsonNode.
         *
         * @param metadata The metadata to set
         * @return The builder
         */
        @JsonSetter
        public T withMetadata(@Nullable final JsonNode metadata) {
            this.bMetadata = metadata;
            return (T) this;
        }

        /**
         * With the ad-hoc metadata to set for the resource as a string of valid JSON.
         *
         * @param metadata The metadata to set. Must be valid JSON
         * @return The builder
         * @throws IllegalArgumentException On invalid JSON
         */
        public T withMetadata(@Nullable final String metadata) throws IllegalArgumentException {
            if (metadata == null) {
                this.bMetadata = null;
            } else {
                try {
                    this.bMetadata = GenieObjectMapper.getMapper().readTree(metadata);
                } catch (final IOException ioe) {
                    throw new IllegalArgumentException("Invalid metadata JSON string passed in " + metadata, ioe);
                }
            }
            return (T) this;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy