/**
* 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.swagger;
import java.util.Map;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Restlet;
import org.restlet.ext.apispark.internal.introspection.DocumentedApplication;
import org.restlet.ext.apispark.internal.model.Section;
import org.restlet.routing.Filter;
import org.restlet.routing.Route;
import org.restlet.routing.Router;
/**
* Swagger enabled application. This subclass of {@link Application} can
* describe itself in the format described by the <a
* href="https://github.com/wordnik/swagger-spec/wiki">Swagger specification
* project</a>. <br>
* <br>
* It requires you to set up a specific end point that serves the resource
* listing and a sub-resource that serves the API declaration of a specific
* resource.<br>
* <br>
* By default, nothing is required. This application adds to its inbound root
* the endpoints ("/api-docs", and "/api-docs/{resource}") required by Swagger
* specification. You can override this behavior by using the
* SwaggerApplication#attachSwagger* methods<br>
*
* By default, both descriptions are generated by introspecting the application
* itself. You can override this behavior by specifying your own implementation
* of {@link SwaggerSpecificationRestlet}.
*
* @author Thierry Boileau
* @see SwaggerSpecificationRestlet
* @see Swagger2SpecificationRestlet
*/
public class SwaggerApplication extends Application implements
DocumentedApplication {
/**
* 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 SwaggerSpecificationRestlet} able to generate Swagger
* documentation.
*
* @param current
* The current Restlet to inspect.
* @return True if the given {@link Restlet} provides a
* {@link SwaggerSpecificationRestlet} able to generate Swagger
* 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 SwaggerSpecificationRestlet) {
documented = true;
}
return documented;
}
/** Indicates if this application can document herself. */
private boolean documented;
/**
* The Sections of the Web API
*/
private Map<String, Section> sections;
/**
* Defines two routes, one for the high level "Resource listing", and the
* other one for the "API declaration". The second route is a sub-resource
* of the first one, defined with the path variable "resource".
*
* @param router
* The router on which defining the new route.
* @param resourceListingPath
* The path to which attach the Restlet that serves the resource
* listing.
* @param resourceListingRestlet
* The Restlet that serves the resource listing.
* @param apiDeclarationPath
* The path to which attach the Restlet that serves the
* declaration of a specific resource.
* @param apiDeclarationRestlet
* The Restlet that serves the declaration of a specific
* resource.
*/
public void attachSwaggerDocumentationRestlets(Router router,
String resourceListingPath, Restlet resourceListingRestlet,
String apiDeclarationPath, Restlet apiDeclarationRestlet) {
router.attach(resourceListingPath, resourceListingRestlet);
router.attach(apiDeclarationPath, apiDeclarationRestlet);
documented = true;
}
/**
* Defines two routes, one for the high level "Resource listing" (by default
* "/api-docs"), and the other one for the "API declaration". The second
* route is a sub-resource of the first one, defined with the path variable
* "resource" (ie "/api-docs/{resource}").
*
* @param router
* The router on which defining the new route.
*
* @see #attachSwaggerSpecificationRestlet(org.restlet.routing.Router,
* String) to attach it with a custom path
*/
public void attachSwaggerSpecificationRestlet(Router router) {
getSwaggerSpecificationRestlet(getContext()).attach(router);
documented = true;
}
/**
* Defines two routes, one for the high level "Resource listing", and the
* other one for the "API declaration". The second route is a sub-resource
* of the first one, defined with the path variable "resource".
*
* @param router
* The router on which defining the new route.
* @param path
* The root path of the documentation Restlet.
*
* @see #attachSwaggerSpecificationRestlet(org.restlet.routing.Router) to
* attach it with the default path
*/
public void attachSwaggerSpecificationRestlet(Router router, String path) {
getSwaggerSpecificationRestlet(getContext()).attach(router, path);
documented = true;
}
/**
* Overrides the parent's implementation. It checks that the application has
* been documented using a {@link SwaggerSpecificationRestlet}. By default,
* the documentation is attached to the high level router, with the
* "/api-docs" 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) {
attachSwaggerSpecificationRestlet(rootRouter);
documented = true;
}
}
}
}
return inboundRoot;
}
@Override
public Map<String, Section> getSections() {
return sections;
}
/**
* The dedicated {@link Restlet} able to generate the Swagger specification
* formats.
*
* @return The {@link Restlet} able to generate the Swagger specification
* formats.
*/
public SwaggerSpecificationRestlet getSwaggerSpecificationRestlet(
Context context) {
SwaggerSpecificationRestlet result = new SwaggerSpecificationRestlet(
context);
result.setApiInboundRoot(this);
return result;
}
}