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 2015 ForgeRock AS. 015 */ 016 017package org.forgerock.json.resource; 018 019import static org.forgerock.json.JsonValue.field; 020import static org.forgerock.json.JsonValue.json; 021import static org.forgerock.json.JsonValue.object; 022 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.LinkedHashMap; 026import java.util.List; 027import java.util.Objects; 028 029import org.forgerock.http.routing.Version; 030import org.forgerock.json.JsonPointer; 031import org.forgerock.json.JsonValue; 032import org.forgerock.util.promise.Promise; 033import org.forgerock.util.promise.Promises; 034 035/** 036 * A utility class containing various factory methods for creating and 037 * manipulating responses. 038 */ 039public final class Responses { 040 041 private Responses() { 042 } 043 044 /** 045 * Returns a new {@code JsonValue} response with the provided JSON content. 046 * 047 * @param json The JSON content. 048 * @return The new {@code ActionResponse}. 049 */ 050 public static ActionResponse newActionResponse(JsonValue json) { 051 return new ActionResponseImpl(json); 052 } 053 054 /** 055 * Returns a new {@code Resource} response with the provided Resource as 056 * content. 057 * 058 * @param id The resource ID if applicable otherwise {@code null}. 059 * @param revision The resource version, if known. 060 * @param content The resource content. 061 * @return The new {@code Resource} response. 062 */ 063 public static ResourceResponse newResourceResponse(String id, String revision, JsonValue content) { 064 return new ResourceResponseImpl(id, revision, content); 065 } 066 067 /** 068 * Creates a new query result with a {@code null} paged results cookie and 069 * no count of the total number of remaining results. 070 * 071 * @return The new {@code QueryResponse}. 072 */ 073 public static QueryResponse newQueryResponse() { 074 return newQueryResponse(null); 075 } 076 077 /** 078 * Creates a new query result with the provided paged results cookie and 079 * no count. 080 * 081 * @param pagedResultsCookie 082 * The opaque cookie which should be used with the next paged 083 * results query request, or {@code null} if paged results were 084 * not requested, or if there are not more pages to be returned. 085 * @return The new {@code QueryResponse}. 086 */ 087 public static QueryResponse newQueryResponse(String pagedResultsCookie) { 088 return newQueryResponse(pagedResultsCookie, CountPolicy.NONE, QueryResponse.NO_COUNT); 089 } 090 091 /** 092 * Creates a new query result with the provided paged results cookie and 093 * a count of the total number of remaining results according to 094 * {@literal totalPagedResultsPolicy}. 095 * 096 * @param pagedResultsCookie 097 * The opaque cookie which should be used with the next paged 098 * results query request, or {@code null} if paged results were 099 * not requested, or if there are not more pages to be returned. 100 * @param totalPagedResultsPolicy 101 * The policy that was used to calculate {@literal totalPagedResults} 102 * @param totalPagedResults 103 * The total number of paged results requested in adherence to 104 * the {@link QueryRequest#getTotalPagedResultsPolicy()} in the request, 105 * or {@link QueryResponse#NO_COUNT} if paged results were not requested, 106 * the count policy is {@code NONE}, or if the total number of remaining 107 * results is unknown. 108 * @return The new {@code QueryResponse}. 109 */ 110 public static QueryResponse newQueryResponse(String pagedResultsCookie, CountPolicy totalPagedResultsPolicy, 111 int totalPagedResults) { 112 return new QueryResponseImpl(pagedResultsCookie, totalPagedResultsPolicy, totalPagedResults, 113 QueryResponse.NO_COUNT); 114 } 115 116 /** 117 * Creates a new query result with the provided paged results cookie and an 118 * estimate of the total number of remaining results. 119 * 120 * @param pagedResultsCookie 121 * The opaque cookie which should be used with the next paged 122 * results query request, or {@code null} if paged results were 123 * not requested, or if there are not more pages to be returned. 124 * @param remainingPagedResults 125 * An estimate of the total number of remaining results to be 126 * returned in subsequent paged results query requests, or 127 * {@code -1} if paged results were not requested, or if the total 128 * number of remaining results is unknown. 129 * 130 * @return The new {@code QueryResponse}. 131 * 132 * @deprecated Use {@link #newQueryResponse(String, CountPolicy, int)} instead. 133 */ 134 @Deprecated 135 public static QueryResponse newRemainingResultsResponse(String pagedResultsCookie, int remainingPagedResults) { 136 return new QueryResponseImpl(pagedResultsCookie, CountPolicy.NONE, QueryResponse.NO_COUNT, 137 remainingPagedResults); 138 } 139 140 private static abstract class AbstractResponseImpl implements Response { 141 private Version resourceApiVersion; 142 143 @Override 144 public void setResourceApiVersion(Version version) { 145 resourceApiVersion = version; 146 } 147 148 @Override 149 public Version getResourceApiVersion() { 150 return resourceApiVersion; 151 } 152 } 153 154 private static final class ActionResponseImpl extends AbstractResponseImpl implements ActionResponse { 155 156 private final JsonValue content; 157 158 private ActionResponseImpl(JsonValue content) { 159 this.content = content; 160 } 161 162 @Override 163 public JsonValue getJsonContent() { 164 return content; 165 } 166 167 @Override 168 public boolean equals(Object o) { 169 if (this == o) { 170 return true; 171 } 172 if (o == null || getClass() != o.getClass()) { 173 return false; 174 } 175 ActionResponse that = (ActionResponse) o; 176 return getJsonContent().getObject().equals(that.getJsonContent().getObject()); 177 } 178 179 @Override 180 public int hashCode() { 181 // FIXME Implies that content is both not null and doesn't wrap null 182 return getJsonContent().getObject().hashCode(); 183 } 184 185 @Override 186 public Promise<ActionResponse, ResourceException> asPromise() { 187 return Promises.<ActionResponse, ResourceException>newResultPromise(this); 188 } 189 190 @Override 191 public String toString() { 192 return json(object(field("content", content.getObject()))).toString(); 193 } 194 195 } 196 197 private static final class ResourceResponseImpl extends AbstractResponseImpl implements ResourceResponse { 198 199 private final JsonValue content; 200 private final String id; 201 private final String revision; 202 private final List<JsonPointer> fields; 203 204 private ResourceResponseImpl(String id, String revision, JsonValue content) { 205 this.id = id; 206 this.revision = revision; 207 this.content = content; 208 this.fields = new ArrayList<JsonPointer>(); 209 } 210 211 @Override 212 public JsonValue getContent() { 213 return content; 214 } 215 216 @Override 217 public String getId() { 218 return id; 219 } 220 221 @Override 222 public String getRevision() { 223 return revision; 224 } 225 226 @Override 227 public List<JsonPointer> getFields() { 228 return Collections.unmodifiableList(fields); 229 } 230 231 @Override 232 public boolean hasFields() { 233 return !fields.isEmpty(); 234 } 235 236 @Override 237 public void addField(JsonPointer... fields) { 238 for (final JsonPointer field : fields) { 239 this.fields.add(field); 240 } 241 } 242 243 public Promise<ResourceResponse, ResourceException> asPromise() { 244 return Promises.<ResourceResponse, ResourceException>newResultPromise(this); 245 } 246 247 @Override 248 public boolean equals(final Object obj) { 249 if (obj == this) { 250 return true; 251 } else if (obj instanceof ResourceResponseImpl) { 252 final ResourceResponseImpl that = (ResourceResponseImpl) obj; 253 return isEqual(id, that.id) && isEqual(revision, that.revision); 254 } else { 255 return false; 256 } 257 } 258 259 private boolean isEqual(final String s1, final String s2) { 260 if (s1 == s2) { 261 return true; 262 } else if (s1 == null || s2 == null) { 263 return false; 264 } else { 265 return s1.equals(s2); 266 } 267 } 268 269 @Override 270 public int hashCode() { 271 final int hash = id != null ? id.hashCode() : 17; 272 return (hash * 31) + (revision != null ? revision.hashCode() : 0); 273 } 274 275 @Override 276 public String toString() { 277 final JsonValue wrapper = new JsonValue(new LinkedHashMap<>(3)); 278 wrapper.add("id", id); 279 wrapper.add("rev", revision); 280 wrapper.add("content", content); 281 return wrapper.toString(); 282 } 283 } 284 285 private static final class QueryResponseImpl extends AbstractResponseImpl implements QueryResponse { 286 287 private final String pagedResultsCookie; 288 private final CountPolicy totalPagedResultsPolicy; 289 private final int totalPagedResults; 290 private final int remainingPagedResults; 291 292 /** 293 * Creates a new query response with the provided paged results cookie and 294 * a count of the total number of resources according to 295 * {@link #totalPagedResultsPolicy}. 296 * 297 * @param pagedResultsCookie 298 * The opaque cookie which should be used with the next paged 299 * results query request, or {@code null} if paged results were 300 * not requested, or if there are not more pages to be returned. 301 * @param totalPagedResultsPolicy 302 * The policy that was used to calculate {@link #totalPagedResults}. 303 * If none is specified ({@code null}), then {@link CountPolicy#NONE} is assumed. 304 * @param totalPagedResults 305 * The total number of paged results requested in adherence to 306 * the {@link QueryRequest#getTotalPagedResultsPolicy()} in the request, 307 * or {@link #NO_COUNT} if paged results were not requested, the count 308 * policy is {@code NONE}, or if the total number of results is unknown. 309 * @param remainingPagedResults 310 * An estimate of the total number of remaining results to be 311 * returned in subsequent paged results query requests, or 312 * {@code -1} if paged results were not requested, or if the total 313 * number of remaining results is unknown. 314 */ 315 private QueryResponseImpl(String pagedResultsCookie, CountPolicy totalPagedResultsPolicy, 316 int totalPagedResults, int remainingPagedResults) { 317 this.pagedResultsCookie = pagedResultsCookie; 318 if (totalPagedResultsPolicy == null) { 319 totalPagedResultsPolicy = CountPolicy.NONE; 320 } 321 this.totalPagedResultsPolicy = totalPagedResultsPolicy; 322 this.totalPagedResults = totalPagedResults; 323 this.remainingPagedResults = remainingPagedResults; 324 } 325 326 @Override 327 public CountPolicy getTotalPagedResultsPolicy() { 328 return totalPagedResultsPolicy; 329 } 330 331 @Override 332 public String getPagedResultsCookie() { 333 return pagedResultsCookie; 334 } 335 336 @Override 337 public int getTotalPagedResults() { 338 return totalPagedResults; 339 } 340 341 @Override 342 public int getRemainingPagedResults() { 343 return remainingPagedResults; 344 } 345 346 @Override 347 public Promise<QueryResponse, ResourceException> asPromise() { 348 return Promises.<QueryResponse, ResourceException>newResultPromise(this); 349 } 350 351 @Override 352 public boolean equals(Object o) { 353 if (this == o) { 354 return true; 355 } 356 if (o == null || getClass() != o.getClass()) { 357 return false; 358 } 359 QueryResponseImpl that = (QueryResponseImpl) o; 360 return totalPagedResults == that.totalPagedResults 361 && Objects.equals(pagedResultsCookie, this.pagedResultsCookie) 362 && totalPagedResultsPolicy == that.totalPagedResultsPolicy 363 && remainingPagedResults == that.remainingPagedResults; 364 } 365 366 @Override 367 public int hashCode() { 368 int result = Objects.hashCode(pagedResultsCookie); 369 result = 31 * result + totalPagedResultsPolicy.hashCode(); 370 result = 31 * result + totalPagedResults; 371 result = 31 * result + remainingPagedResults; 372 return result; 373 } 374 375 @Override 376 public String toString() { 377 final JsonValue wrapper = new JsonValue(new LinkedHashMap<>(4)); 378 wrapper.add(FIELD_TOTAL_PAGED_RESULTS, totalPagedResults); 379 wrapper.add(FIELD_TOTAL_PAGED_RESULTS_POLICY, totalPagedResultsPolicy); 380 wrapper.add(FIELD_REMAINING_PAGED_RESULTS, remainingPagedResults); 381 wrapper.add(FIELD_PAGED_RESULTS_COOKIE, pagedResultsCookie); 382 return wrapper.toString(); 383 } 384 } 385}