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}