001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2016 ForgeRock AS.
015 * Portions Copyright 2018 Wren Security
016 */
017
018package org.forgerock.api.models;
019
020import static org.forgerock.api.util.ValidationUtil.*;
021import static org.forgerock.util.Reject.*;
022import com.fasterxml.jackson.annotation.JsonAnySetter;
023import com.fasterxml.jackson.annotation.JsonIgnore;
024import com.fasterxml.jackson.annotation.JsonValue;
025import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
026
027import java.util.Map;
028import java.util.Objects;
029import java.util.Set;
030import java.util.TreeMap;
031
032/**
033 * Class that represents API descriptor's Service {@link Resource} definitions.
034 */
035@JsonDeserialize(builder = Services.Builder.class)
036public final class Services {
037
038    private final Map<String, Resource> services;
039
040    private Services(Builder builder) {
041        this.services = builder.services;
042    }
043
044    /**
045     * Gets a {@code Map} of service-names to {@link Resource}s.
046     * This method is currently only used for JSON serialization.
047     *
048     * @return {@code Map} of service-names to {@link Resource}s.
049     */
050    @JsonValue
051    protected Map<String, Resource> getServices() {
052        return services;
053    }
054
055    /**
056     * Gets the {@link Resource} for a given service-name.
057     *
058     * @param name Service name
059     * @return {@link Schema} or {@code null} if does-not-exist.
060     */
061    @JsonIgnore
062    public Resource get(String name) {
063        return services.get(name);
064    }
065
066    /**
067     * Returns all {@link Services} names.
068     *
069     * @return All {@link Services} names.
070     */
071    @JsonIgnore
072    public Set<String> getNames() {
073        return services.keySet();
074    }
075
076    /**
077     * Create a new Builder for Services.
078     *
079     * @return Builder
080     */
081
082    public static Builder services() {
083        return new Builder();
084    }
085
086    @Override
087    public boolean equals(Object o) {
088        if (this == o) {
089            return true;
090        }
091        if (o == null || getClass() != o.getClass()) {
092            return false;
093        }
094        Services services1 = (Services) o;
095        return Objects.equals(services, services1.services);
096    }
097
098    @Override
099    public int hashCode() {
100        return Objects.hash(services);
101    }
102
103    /**
104     * Builder to help construct the Services.
105     */
106    public static final class Builder {
107
108        private final Map<String, Resource> services = new TreeMap<>();
109
110        /**
111         * Private default constructor.
112         */
113        private Builder() {
114        }
115
116        /**
117         * Adds a {@link Resource}.
118         *
119         * @param name Service name
120         * @param resource {@link Resource}
121         * @return Builder
122         */
123        @JsonAnySetter
124        public Builder put(String name, Resource resource) {
125            if (isEmpty(name) || containsWhitespace(name)) {
126                throw new IllegalArgumentException(
127                        "Resource name is required, must not be blank, and must not contain " +
128                        "whitespace; given: '" + name + "'");
129            }
130            if (services.containsKey(name) && !services.get(name).equals(resource)) {
131                throw new IllegalStateException(
132                        "Resource name already exists but Resource objects are not equal; " +
133                        "given: '" + name + "'");
134            }
135
136            services.put(name, checkNotNull(resource));
137            return this;
138        }
139
140        /**
141         * Builds the Definitions instance.
142         *
143         * @return Definitions instance
144         */
145        public Services build() {
146            return new Services(this);
147        }
148    }
149
150
151}