/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.ext.raml; import org.restlet.Application; import org.restlet.Context; import org.restlet.Restlet; import org.restlet.routing.Filter; import org.restlet.routing.Route; import org.restlet.routing.Router; /** * RAML enabled application. This subclass of {@link Application} can describe * itself in the format described by the <a * href="http://raml.org/spec.html">RAML specification project</a>.<br> * <br> * It requires you to set up a specific end point that serves the RAML * definition.<br> * <br> * By default, nothing is required. This application adds to its inbound root * the endpoint ("/raml"). You can override this behavior by using the * RamlApplication#attachRaml* methods<br> * * By default, the description is generated by introspecting the application * itself. You can override this behavior by specifying your own implementation * of {@link RamlSpecificationRestlet}. * * @author Cyprien Quilici * */ public class RamlApplication extends Application { /** * Returns the next router available. * * @param current * The current Restlet to inspect. * @return The first router available. */ private static Router getNextRouter(Restlet current) { Router result = null; if (current instanceof Router) { result = (Router) current; } else if (current instanceof Filter) { result = getNextRouter(((Filter) current).getNext()); } return result; } /** * Indicates if the given {@link Restlet} provides a * {@link RamlSpecificationRestlet} able to generate RAML documentation. * * @param current * The current Restlet to inspect. * @return True if the given {@link Restlet} provides a * {@link RamlSpecificationRestlet} able to generate RAML * documentation. */ private static boolean isDocumented(Restlet current) { boolean documented = false; Router router = null; if (current instanceof Router) { router = (Router) current; for (Route route : router.getRoutes()) { if (isDocumented(route.getNext())) { documented = true; break; } } } else if (current instanceof Filter) { documented = isDocumented(((Filter) current).getNext()); } else if (current instanceof RamlSpecificationRestlet) { documented = true; } return documented; } /** Indicates if this application can document herself. */ private boolean documented; /** * Defines the route on which the Raml definition will be provided. * * @param router * The router on which defining the new route. * @param ramlPath * The path to which attach the Restlet that serves the RAML * definition. * @param ramlRestlet * The Restlet that serves the RAML definition. */ public void attachRamlDocumentationRestlet(Router router, String ramlPath, Restlet ramlRestlet) { router.attach(ramlPath, ramlRestlet); documented = true; } /** * Defines the route on which the RAML definition will be provided (by * default, "/raml"). * * @param router * The router on which defining the new route. */ public void attachRamlSpecificationRestlet(Router router) { getRamlSpecificationRestlet(getContext()).attach(router); documented = true; } /** * Overrides the parent's implementation. It checks that the application has * been documented using a {@link RamlSpecificationRestlet}. By default, the * documentation is attached to the high level router, with the "/raml" * path. */ @Override public Restlet getInboundRoot() { Restlet inboundRoot = super.getInboundRoot(); if (!documented) { synchronized (this) { if (!documented) { Router rootRouter = getNextRouter(inboundRoot); // Check that the application has been documented. documented = isDocumented(rootRouter); if (!documented) { attachRamlSpecificationRestlet(rootRouter); documented = true; } } } } return inboundRoot; } /** * The dedicated {@link Restlet} able to generate the RAML specification * format. * * @return The {@link Restlet} able to generate the RAML specification * format. */ public RamlSpecificationRestlet getRamlSpecificationRestlet(Context context) { RamlSpecificationRestlet result = new RamlSpecificationRestlet(context); result.setApiInboundRoot(this); return result; } }