CrestHttpApplication.java
/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2015-2016 ForgeRock AS.
*/
package org.forgerock.json.resource.http.examples;
import static org.forgerock.http.routing.RoutingMode.*;
import static org.forgerock.json.resource.Applications.*;
import static org.forgerock.json.resource.Requests.*;
import static org.forgerock.json.resource.Resources.*;
import static org.forgerock.json.resource.RouteMatchers.*;
import static org.forgerock.json.resource.http.CrestHttp.*;
import org.asciidoctor.Asciidoctor;
import org.asciidoctor.AttributesBuilder;
import org.asciidoctor.OptionsBuilder;
import org.asciidoctor.Placement;
import org.asciidoctor.SafeMode;
import org.forgerock.api.markup.ApiDocGenerator;
import org.forgerock.api.models.ApiDescription;
import org.forgerock.http.DescribedHttpApplication;
import org.forgerock.http.Handler;
import org.forgerock.http.HttpApplicationException;
import org.forgerock.http.example.DescribedOauth2Endpoint;
import org.forgerock.http.header.ContentTypeHeader;
import org.forgerock.http.io.Buffer;
import org.forgerock.http.protocol.Request;
import org.forgerock.http.protocol.Response;
import org.forgerock.http.protocol.Status;
import org.forgerock.http.routing.RouteMatchers;
import org.forgerock.http.routing.RoutingMode;
import org.forgerock.http.swagger.SwaggerApiProducer;
import org.forgerock.http.util.Uris;
import org.forgerock.json.resource.MemoryBackend;
import org.forgerock.json.resource.ResourcePath;
import org.forgerock.json.resource.Router;
import org.forgerock.json.resource.descriptor.examples.handler.UserCollectionHandler;
import org.forgerock.http.ApiProducer;
import org.forgerock.services.context.Context;
import org.forgerock.services.routing.DelegatingRouteMatcher;
import org.forgerock.services.routing.RouteMatch;
import org.forgerock.util.Factory;
import org.forgerock.util.promise.NeverThrowsException;
import org.forgerock.util.promise.Promise;
import io.swagger.models.Info;
import io.swagger.models.Scheme;
import io.swagger.models.Swagger;
/**
* Http Application implementation to demonstrate integration with the Commons HTTP Framework.
*/
public class CrestHttpApplication implements DescribedHttpApplication {
private static final String SWAGGER_JSON_ROUTE = "../..?_api";
private static final ContentTypeHeader HTML_CONTENT_TYPE_HEADER =
ContentTypeHeader.valueOf("text/html; charset=UTF-8");
@Override
public Handler start() throws HttpApplicationException {
final Asciidoctor asciidoctor = Asciidoctor.Factory.create();
final Router crestRouter = new Router();
crestRouter.addRoute(requestUriMatcher(STARTS_WITH, "/users"), newHandler(new MemoryBackend()));
crestRouter.addRoute(requestUriMatcher(STARTS_WITH, "/groups"), newHandler(new MemoryBackend()));
crestRouter.addRoute(requestUriMatcher(STARTS_WITH, "/api/users"), UserCollectionHandler.getUsersRouter());
crestRouter.addRoute(requestUriMatcher(STARTS_WITH, "/api/admins"), UserCollectionHandler.getAdminsRouter());
Handler crestHandler = newHttpHandler(simpleCrestApplication(newInternalConnectionFactory(crestRouter),
"frapi:example", "1.0"));
final org.forgerock.http.routing.Router router = new org.forgerock.http.routing.Router();
router.setDefaultRoute(crestHandler);
router.addRoute(RouteMatchers.requestUriMatcher(RoutingMode.STARTS_WITH, "/chf/oauth2"),
new DescribedOauth2Endpoint());
// convert ApiDescription to HTML documentation
router.addRoute(RouteMatchers.requestUriMatcher(RoutingMode.STARTS_WITH, "/docs/html"),
new Handler() {
@Override
public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
ApiDescription apiDescription = crestRouter.handleApiRequest(context,
newApiRequest(ResourcePath.empty()));
final String asciiDocMarkup = ApiDocGenerator.execute("Users and Devices API", apiDescription,
null);
final String html;
synchronized (asciidoctor) {
html = asciidoctor.render(asciiDocMarkup,
OptionsBuilder.options()
.attributes(AttributesBuilder.attributes()
.tableOfContents(Placement.LEFT)
.sectNumLevels(5)
.attribute("toclevels", 5)
.get())
.safe(SafeMode.SAFE)
.headerFooter(true)
.get());
}
final Response response = new Response(Status.OK);
response.getHeaders().add(HTML_CONTENT_TYPE_HEADER);
response.setEntity(html);
return Response.newResponsePromise(response);
}
});
// redirect to Swagger UI page, given a URL parameter to point to the Swagger JSON endpoint
router.addRoute(RouteMatchers.requestUriMatcher(RoutingMode.EQUALS, "/docs/api"),
new Handler() {
@Override
public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
final String uri = request.getUri().toString();
final String baseUrl = uri.substring(0, uri.indexOf("/docs/api"));
final String url = baseUrl + "/openapi/index.html?url="
+ Uris.urlEncodeQueryParameterNameOrValue(SWAGGER_JSON_ROUTE)
+ "&title=" + Uris.urlEncodeQueryParameterNameOrValue("Users and Devices API");
final Response response = new Response(Status.FOUND);
response.getHeaders().add("Location", url);
return Response.newResponsePromise(response);
}
});
// simple page providing links to HTML docs and Swagger UI
router.addRoute(new DelegatingRouteMatcher<Request>(RouteMatchers.requestUriMatcher(RoutingMode.EQUALS, "/")) {
@Override
public RouteMatch evaluate(Context context, Request request) {
if (request.getForm().containsKey("_crestapi")) {
return null;
}
return super.evaluate(context, request);
}
}, new Handler() {
@Override
public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
final String html = "<!DOCTYPE html><html><head><title>CREST Examples</title></head><body>"
+ "<p><a href=\"?_api\">Users and Devices API OpenAPI JSON</a></p>"
+ "<p><a href=\"?_crestapi\">Users and Devices API CREST Descriptor JSON</a></p>"
+ "<p><a href=\"./docs/api\">Users and Devices API explorer</a></p>"
+ "<p><a href=\"./docs/html\">Users and Devices API documentation</a></p>"
+ "</body></html>";
final Response response = new Response(Status.OK);
response.getHeaders().add(HTML_CONTENT_TYPE_HEADER);
response.setEntity(html);
return Response.newResponsePromise(response);
}
});
return router;
}
@Override
public Factory<Buffer> getBufferFactory() {
return null;
}
@Override
public void stop() {
// empty
}
@Override
public ApiProducer<Swagger> getApiProducer() {
return new SwaggerApiProducer(new Info().title("CREST Examples"), null, null, Scheme.HTTP);
}
}