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 2014-2015 ForgeRock AS. 015 */ 016 017package org.forgerock.json.resource.examples; 018 019import static org.forgerock.http.routing.Version.version; 020import static org.forgerock.json.resource.Requests.newCreateRequest; 021import static org.forgerock.json.resource.Resources.newInternalConnection; 022import static org.forgerock.json.resource.RouteMatchers.requestResourceApiVersionMatcher; 023import static org.forgerock.json.resource.RouteMatchers.requestUriMatcher; 024import static org.forgerock.json.resource.Router.uriTemplate; 025import static org.forgerock.json.resource.examples.DemoUtils.*; 026 027import org.forgerock.services.context.Context; 028import org.forgerock.http.routing.RoutingMode; 029import org.forgerock.json.resource.ActionRequest; 030import org.forgerock.json.resource.ActionResponse; 031import org.forgerock.json.resource.Connection; 032import org.forgerock.json.resource.ConnectionFactory; 033import org.forgerock.json.resource.CreateRequest; 034import org.forgerock.json.resource.DeleteRequest; 035import org.forgerock.json.resource.MemoryBackend; 036import org.forgerock.json.resource.PatchRequest; 037import org.forgerock.json.resource.QueryRequest; 038import org.forgerock.json.resource.QueryResourceHandler; 039import org.forgerock.json.resource.QueryResponse; 040import org.forgerock.json.resource.ReadRequest; 041import org.forgerock.json.resource.RequestHandler; 042import org.forgerock.json.resource.Requests; 043import org.forgerock.json.resource.ResourceException; 044import org.forgerock.json.resource.ResourceResponse; 045import org.forgerock.json.resource.Resources; 046import org.forgerock.json.resource.Router; 047import org.forgerock.json.resource.SingletonResourceProvider; 048import org.forgerock.json.resource.UpdateRequest; 049import org.forgerock.util.promise.Promise; 050 051/** 052 * An example client application which performs asynchronous reads against different versions of the same resource. 053 * 054 * @since 2.4.0 055 */ 056public final class VersionedResourcesDemo { 057 058 private VersionedResourcesDemo() { 059 // No implementation. 060 } 061 062 /** 063 * Main application. 064 * 065 * @param args 066 * CLI arguments. 067 * @throws ResourceException 068 * If an unexpected error occurs. 069 */ 070 public static void main(String... args) throws ResourceException { 071 try (ConnectionFactory server = getConnectionFactory(); 072 Connection connection = server.getConnection()) { 073 log("Reading version 1.0 of resource"); 074 ResourceResponse response = connection.read(ctx(), Requests.newReadRequest("users/1") 075 .setResourceVersion(version(1))); 076 log("Retrieved resource with revision: " + response.getRevision()); 077 078 079 log("Reading version 1.5 of resource"); 080 response = connection.read(ctx(), Requests.newReadRequest("users/1") 081 .setResourceVersion(version(1, 5))); 082 log("Retrieved resource with revision: " + response.getRevision()); 083 084 log("Reading version 2.0 of resource"); 085 response = connection.read(ctx(), Requests.newReadRequest("users/1") 086 .setResourceVersion(version(2))); 087 log("Retrieved resource with revision: " + response.getRevision()); 088 } 089 } 090 091 private static ConnectionFactory getConnectionFactory() throws ResourceException { 092 MemoryBackend usersV1Dot0 = new MemoryBackend(); 093 MemoryBackend usersV1Dot5 = new MemoryBackend(); 094 MemoryBackend usersV2Dot0 = new MemoryBackend(); 095 096 RequestHandler rolesV1Dot0 = handler(new MemoryBackend()); 097 RequestHandler rolesV1Dot5 = handler(new MemoryBackend()); 098 RequestHandler rolesV2Dot0 = handler(new MemoryBackend()); 099 100 SingletonResourceProvider configV1Dot0 = singleton(new MemoryBackend()); 101 SingletonResourceProvider configV1Dot5 = singleton(new MemoryBackend()); 102 SingletonResourceProvider configV2Dot0 = singleton(new MemoryBackend()); 103 104 MemoryBackend groups = new MemoryBackend(); 105 106 Router router = new Router(); 107 Router usersRouter = new Router(); 108 router.addRoute(requestUriMatcher(RoutingMode.STARTS_WITH, "/users"), usersRouter); 109 usersRouter.addRoute(version(1), usersV1Dot0); 110 usersRouter.addRoute(version(1, 5), usersV1Dot5); 111 usersRouter.addRoute(version(2), usersV2Dot0); 112 113 Router rolesRouter = new Router(); 114 router.addRoute(requestUriMatcher(RoutingMode.EQUALS, "/roles"), rolesRouter); 115 rolesRouter.addRoute(requestResourceApiVersionMatcher(version(1)), rolesV1Dot0); 116 rolesRouter.addRoute(requestResourceApiVersionMatcher(version(1, 5)), rolesV1Dot5); 117 rolesRouter.addRoute(requestResourceApiVersionMatcher(version(2)), rolesV2Dot0); 118 119 Router configRouter = new Router(); 120 router.addRoute(requestUriMatcher(RoutingMode.STARTS_WITH, "/config"), configRouter); 121 configRouter.addRoute(version(1), configV1Dot0); 122 configRouter.addRoute(version(1, 5), configV1Dot5); 123 configRouter.addRoute(version(2), configV2Dot0); 124 125 // Ignores any version information. 126 router.addRoute(uriTemplate("groups"), groups); 127 128 final Connection connection = newInternalConnection(router); 129 connection.create(ctx(), newCreateRequest("users", "1", userAliceWithIdAndRev(1, 0)) 130 .setResourceVersion(version(1))); 131 connection.create(ctx(), newCreateRequest("users", "1", userAliceWithIdAndRev(1, 1)) 132 .setResourceVersion(version(1, 5))); 133 connection.create(ctx(), newCreateRequest("users", "1", userAliceWithIdAndRev(1, 2)) 134 .setResourceVersion(version(2))); 135 136 return Resources.newInternalConnectionFactory(router); 137 } 138 139 private static SingletonResourceProvider singleton(final MemoryBackend backend) { 140 return new SingletonResourceProvider() { 141 142 @Override 143 public Promise<ActionResponse, ResourceException> actionInstance(Context context, ActionRequest request) { 144 return backend.actionInstance(context, "INSTANCE", request); 145 } 146 147 @Override 148 public Promise<ResourceResponse, ResourceException> patchInstance(Context context, PatchRequest request) { 149 return backend.patchInstance(context, "INSTANCE", request); 150 } 151 152 @Override 153 public Promise<ResourceResponse, ResourceException> readInstance(Context context, ReadRequest request) { 154 return backend.readInstance(context, "INSTANCE", request); 155 } 156 157 @Override 158 public Promise<ResourceResponse, ResourceException> updateInstance(Context context, UpdateRequest request) { 159 return backend.updateInstance(context, "INSTANCE", request); 160 } 161 }; 162 } 163 164 private static RequestHandler handler(final MemoryBackend backend) { 165 return new RequestHandler() { 166 @Override 167 public Promise<ActionResponse, ResourceException> handleAction(Context context, ActionRequest request) { 168 return backend.actionInstance(context, "INSTANCE", request); 169 } 170 171 @Override 172 public Promise<ResourceResponse, ResourceException> handleCreate(Context context, CreateRequest request) { 173 return backend.createInstance(context, request); 174 } 175 176 @Override 177 public Promise<ResourceResponse, ResourceException> handleDelete(Context context, DeleteRequest request) { 178 return backend.deleteInstance(context, "INSTANCE", request); 179 } 180 181 @Override 182 public Promise<ResourceResponse, ResourceException> handlePatch(Context context, PatchRequest request) { 183 return backend.patchInstance(context, "INSTANCE", request); 184 } 185 186 @Override 187 public Promise<QueryResponse, ResourceException> handleQuery(Context context, QueryRequest request, 188 QueryResourceHandler handler) { 189 return backend.queryCollection(context, request, handler); 190 } 191 192 @Override 193 public Promise<ResourceResponse, ResourceException> handleRead(Context context, ReadRequest request) { 194 return backend.readInstance(context, "INSTANCE", request); 195 } 196 197 @Override 198 public Promise<ResourceResponse, ResourceException> handleUpdate(Context context, UpdateRequest request) { 199 return backend.updateInstance(context, "INSTANCE", request); 200 } 201 }; 202 } 203}