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 */ 016 017package org.forgerock.api.models; 018 019import java.util.HashMap; 020import java.util.Map; 021import java.util.Objects; 022import java.util.Set; 023 024import com.fasterxml.jackson.annotation.JsonAnySetter; 025import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 026import org.forgerock.api.ApiValidationException; 027import org.forgerock.http.routing.Version; 028import org.forgerock.util.Reject; 029 030import com.fasterxml.jackson.annotation.JsonIgnore; 031import com.fasterxml.jackson.annotation.JsonValue; 032 033/** 034 * Class that represents versioned {@link Resource}s on an API descriptor path. 035 */ 036@JsonDeserialize(builder = VersionedPath.Builder.class) 037public final class VersionedPath { 038 039 /** 040 * Version {@code 0.0} represents null/empty, for when resource versions are not required by an API (e.g., OpenIDM). 041 */ 042 public static final Version UNVERSIONED = Version.version(0); 043 044 private final Map<Version, Resource> paths; 045 046 private VersionedPath(Builder builder) { 047 this.paths = builder.paths; 048 049 if (paths.isEmpty()) { 050 throw new ApiValidationException("Must have at least one versioned resource"); 051 } 052 if (paths.size() > 1) { 053 for (final Version version : paths.keySet()) { 054 if (UNVERSIONED.equals(version)) { 055 throw new ApiValidationException("Version 0.0 (unversioned) must be the only version when used"); 056 } 057 } 058 } 059 } 060 061 /** 062 * Gets a {@code Map} of versions to {@link Resource}s. This method is currently only used for JSON serialization. 063 * 064 * @return {@code Map} of versions to {@link Resource}s. 065 */ 066 @JsonValue 067 protected Map<Version, Resource> getPaths() { 068 return paths; 069 } 070 071 /** 072 * Gets the {@link Resource} for a given version. 073 * 074 * @param version Resource version 075 * @return {@link Resource} or {@code null} if does-not-exist. 076 */ 077 @JsonIgnore 078 public Resource get(Version version) { 079 return paths.get(version); 080 } 081 082 /** 083 * Returns all resource-versions on this path. 084 * 085 * @return All resource-versions. 086 */ 087 @JsonIgnore 088 public Set<Version> getVersions() { 089 return paths.keySet(); 090 } 091 092 /** 093 * Create a new Builder for VersionedPath. 094 * 095 * @return Builder 096 */ 097 public static Builder versionedPath() { 098 return new Builder(); 099 } 100 101 /** 102 * Allows for mutation of paths when merging {@code Paths} instances. 103 * @param v The version. 104 * @param resource The resource. 105 */ 106 void addVersion(Version v, Resource resource) { 107 if (paths.containsKey(v)) { 108 throw new IllegalArgumentException("Trying to redefine version: " + v); 109 } 110 paths.put(v, Reject.checkNotNull(resource)); 111 } 112 113 @Override 114 public boolean equals(Object o) { 115 if (this == o) { 116 return true; 117 } 118 if (o == null || getClass() != o.getClass()) { 119 return false; 120 } 121 VersionedPath that = (VersionedPath) o; 122 return Objects.equals(paths, that.paths); 123 } 124 125 @Override 126 public int hashCode() { 127 return Objects.hash(paths); 128 } 129 130 /** 131 * Builder to help construct the VersionedPath. 132 */ 133 public static final class Builder { 134 135 private final Map<Version, Resource> paths = new HashMap<>(); 136 137 /** 138 * Private default constructor. 139 */ 140 private Builder() { 141 } 142 143 /** 144 * Adds a resource-version. 145 * 146 * @param version Resource-version 147 * @param resource {@link Resource} 148 * @return Builder 149 */ 150 public Builder put(Version version, Resource resource) { 151 if (paths.containsKey(version)) { 152 throw new IllegalStateException("version not unique"); 153 } 154 paths.put(Reject.checkNotNull(version), Reject.checkNotNull(resource)); 155 return this; 156 } 157 158 /** 159 * Adds a resource-version. 160 * 161 * @param version Resource-version as string 162 * @param resource {@link Resource} 163 * @return Builder 164 */ 165 @JsonAnySetter 166 public Builder put(String version, Resource resource) { 167 return put(Version.version(version), resource); 168 } 169 170 /** 171 * Builds the VersionedPath instance. 172 * 173 * @return VersionedPath instance 174 */ 175 public VersionedPath build() { 176 return new VersionedPath(this); 177 } 178 } 179 180}