View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2015-2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.resource.http.examples;
18  
19  import static org.forgerock.http.routing.RoutingMode.*;
20  import static org.forgerock.json.resource.Applications.*;
21  import static org.forgerock.json.resource.Requests.*;
22  import static org.forgerock.json.resource.Resources.*;
23  import static org.forgerock.json.resource.RouteMatchers.*;
24  import static org.forgerock.json.resource.http.CrestHttp.*;
25  
26  import org.asciidoctor.Asciidoctor;
27  import org.asciidoctor.AttributesBuilder;
28  import org.asciidoctor.OptionsBuilder;
29  import org.asciidoctor.Placement;
30  import org.asciidoctor.SafeMode;
31  import org.forgerock.api.markup.ApiDocGenerator;
32  import org.forgerock.api.models.ApiDescription;
33  import org.forgerock.http.DescribedHttpApplication;
34  import org.forgerock.http.Handler;
35  import org.forgerock.http.HttpApplicationException;
36  import org.forgerock.http.example.DescribedOauth2Endpoint;
37  import org.forgerock.http.header.ContentTypeHeader;
38  import org.forgerock.http.io.Buffer;
39  import org.forgerock.http.protocol.Request;
40  import org.forgerock.http.protocol.Response;
41  import org.forgerock.http.protocol.Status;
42  import org.forgerock.http.routing.RouteMatchers;
43  import org.forgerock.http.routing.RoutingMode;
44  import org.forgerock.http.swagger.SwaggerApiProducer;
45  import org.forgerock.http.util.Uris;
46  import org.forgerock.json.resource.MemoryBackend;
47  import org.forgerock.json.resource.ResourcePath;
48  import org.forgerock.json.resource.Router;
49  import org.forgerock.json.resource.descriptor.examples.handler.UserCollectionHandler;
50  import org.forgerock.http.ApiProducer;
51  import org.forgerock.services.context.Context;
52  import org.forgerock.services.routing.DelegatingRouteMatcher;
53  import org.forgerock.services.routing.RouteMatch;
54  import org.forgerock.util.Factory;
55  import org.forgerock.util.promise.NeverThrowsException;
56  import org.forgerock.util.promise.Promise;
57  
58  import io.swagger.models.Info;
59  import io.swagger.models.Scheme;
60  import io.swagger.models.Swagger;
61  
62  /**
63   * Http Application implementation to demonstrate integration with the Commons HTTP Framework.
64   */
65  public class CrestHttpApplication implements DescribedHttpApplication {
66  
67      private static final String SWAGGER_JSON_ROUTE = "../..?_api";
68  
69      private static final ContentTypeHeader HTML_CONTENT_TYPE_HEADER =
70              ContentTypeHeader.valueOf("text/html; charset=UTF-8");
71  
72      @Override
73      public Handler start() throws HttpApplicationException {
74          final Asciidoctor asciidoctor = Asciidoctor.Factory.create();
75  
76          final Router crestRouter = new Router();
77          crestRouter.addRoute(requestUriMatcher(STARTS_WITH, "/users"), newHandler(new MemoryBackend()));
78          crestRouter.addRoute(requestUriMatcher(STARTS_WITH, "/groups"), newHandler(new MemoryBackend()));
79          crestRouter.addRoute(requestUriMatcher(STARTS_WITH, "/api/users"), UserCollectionHandler.getUsersRouter());
80          crestRouter.addRoute(requestUriMatcher(STARTS_WITH, "/api/admins"), UserCollectionHandler.getAdminsRouter());
81  
82          Handler crestHandler = newHttpHandler(simpleCrestApplication(newInternalConnectionFactory(crestRouter),
83                  "frapi:example", "1.0"));
84  
85          final org.forgerock.http.routing.Router router = new org.forgerock.http.routing.Router();
86          router.setDefaultRoute(crestHandler);
87          router.addRoute(RouteMatchers.requestUriMatcher(RoutingMode.STARTS_WITH, "/chf/oauth2"),
88                  new DescribedOauth2Endpoint());
89  
90          // convert ApiDescription to HTML documentation
91          router.addRoute(RouteMatchers.requestUriMatcher(RoutingMode.STARTS_WITH, "/docs/html"),
92                  new Handler() {
93                      @Override
94                      public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
95                          ApiDescription apiDescription = crestRouter.handleApiRequest(context,
96                                  newApiRequest(ResourcePath.empty()));
97                          final String asciiDocMarkup = ApiDocGenerator.execute("Users and Devices API", apiDescription,
98                                  null);
99  
100                         final String html;
101                         synchronized (asciidoctor) {
102                             html = asciidoctor.render(asciiDocMarkup,
103                                     OptionsBuilder.options()
104                                             .attributes(AttributesBuilder.attributes()
105                                                     .tableOfContents(Placement.LEFT)
106                                                     .sectNumLevels(5)
107                                                     .attribute("toclevels", 5)
108                                                     .get())
109                                             .safe(SafeMode.SAFE)
110                                             .headerFooter(true)
111                                             .get());
112                         }
113 
114                         final Response response = new Response(Status.OK);
115                         response.getHeaders().add(HTML_CONTENT_TYPE_HEADER);
116                         response.setEntity(html);
117                         return Response.newResponsePromise(response);
118                     }
119                 });
120 
121         // redirect to Swagger UI page, given a URL parameter to point to the Swagger JSON endpoint
122         router.addRoute(RouteMatchers.requestUriMatcher(RoutingMode.EQUALS, "/docs/api"),
123                 new Handler() {
124                     @Override
125                     public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
126                         final String uri = request.getUri().toString();
127                         final String baseUrl = uri.substring(0, uri.indexOf("/docs/api"));
128                         final String url = baseUrl + "/openapi/index.html?url="
129                                 + Uris.urlEncodeQueryParameterNameOrValue(SWAGGER_JSON_ROUTE)
130                                 + "&title=" + Uris.urlEncodeQueryParameterNameOrValue("Users and Devices API");
131 
132                         final Response response = new Response(Status.FOUND);
133                         response.getHeaders().add("Location", url);
134                         return Response.newResponsePromise(response);
135                     }
136                 });
137 
138         // simple page providing links to HTML docs and Swagger UI
139         router.addRoute(new DelegatingRouteMatcher<Request>(RouteMatchers.requestUriMatcher(RoutingMode.EQUALS, "/")) {
140             @Override
141             public RouteMatch evaluate(Context context, Request request) {
142                 if (request.getForm().containsKey("_crestapi")) {
143                     return null;
144                 }
145                 return super.evaluate(context, request);
146             }
147         }, new Handler() {
148             @Override
149             public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
150                 final String html = "<!DOCTYPE html><html><head><title>CREST Examples</title></head><body>"
151                         + "<p><a href=\"?_api\">Users and Devices API OpenAPI JSON</a></p>"
152                         + "<p><a href=\"?_crestapi\">Users and Devices API CREST Descriptor JSON</a></p>"
153                         + "<p><a href=\"./docs/api\">Users and Devices API explorer</a></p>"
154                         + "<p><a href=\"./docs/html\">Users and Devices API documentation</a></p>"
155                         + "</body></html>";
156 
157                 final Response response = new Response(Status.OK);
158                 response.getHeaders().add(HTML_CONTENT_TYPE_HEADER);
159                 response.setEntity(html);
160                 return Response.newResponsePromise(response);
161             }
162         });
163         return router;
164     }
165 
166     @Override
167     public Factory<Buffer> getBufferFactory() {
168         return null;
169     }
170 
171     @Override
172     public void stop() {
173         // empty
174     }
175 
176     @Override
177     public ApiProducer<Swagger> getApiProducer() {
178         return new SwaggerApiProducer(new Info().title("CREST Examples"), null, null, Scheme.HTTP);
179     }
180 }