All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.muserver.rest.RestHandlerBuilder Maven / Gradle / Ivy

There is a newer version: 2.0.3
Show newest version
package io.muserver.rest;

import io.muserver.MuHandlerBuilder;
import io.muserver.openapi.InfoObject;
import io.muserver.openapi.OpenAPIObjectBuilder;

import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.ext.*;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Stream;

import static io.muserver.openapi.PathsObjectBuilder.pathsObject;

/**
 * Used to create a {@link RestHandler} for handling JAX-RS REST resources.
 * @see #restHandler(Object...)
 */
public class RestHandlerBuilder implements MuHandlerBuilder {

    private Object[] resources;
    private final List customWriters = new ArrayList<>();
    private final List customReaders = new ArrayList<>();
    private final List customParamConverterProviders = new ArrayList<>();
    private String openApiJsonUrl = null;
    private String openApiHtmlUrl = null;
    private OpenAPIObjectBuilder openAPIObject;
    private String openApiHtmlCss = null;
    private Map, ExceptionMapper> exceptionMappers = new HashMap<>();
    private List preMatchRequestFilters = new ArrayList<>();
    private List requestFilters = new ArrayList<>();
    private List responseFilters = new ArrayList<>();
    private CORSConfig corsConfig = CORSConfigBuilder.disabled().build();

    public RestHandlerBuilder(Object... resources) {
        this.resources = resources;
    }

    /**
     * Adds one or more rest resources to this handler
     * @param resources One or more instances of classes that are decorated with {@link javax.ws.rs.Path} annotations.
     * @return This builder
     */
    public RestHandlerBuilder addResource(Object... resources) {
        this.resources = Stream.of(this.resources, resources).flatMap(Stream::of).toArray(Object[]::new);
        return this;
    }

    /**
     * 

Registers an object that can write custom classes to responses.

*

For example, if you return an instance of MyClass from a REST method, you need to specify how * that gets serialised with a MessageBodyWriter<MyClass> writer.

* @param The type of object that the writer can serialise * @param writer A response body writer * @return This builder */ public RestHandlerBuilder addCustomWriter(MessageBodyWriter writer) { customWriters.add(writer); return this; } /** *

Registers an object that can deserialise request bodies into custom classes.

*

For example, if you specify that the request body is a MyClass, you need to specify how * that gets deserialised with a MessageBodyReader<MyClass> reader.

* @param The type of object that the reader can deserialise * @param reader A request body reader * @return This builder */ public RestHandlerBuilder addCustomReader(MessageBodyReader reader) { customReaders.add(reader); return this; } /** *

Registers an object that can convert rest method parameters (e.g. querystring, header, form or path params) * into custom classes.

*

In most cases, it is easier to instead use {@link #addCustomParamConverter(Class, ParamConverter)}

* @param paramConverterProvider A provider of parameter converters * @return This builder */ public RestHandlerBuilder addCustomParamConverterProvider(ParamConverterProvider paramConverterProvider) { customParamConverterProviders.add(paramConverterProvider); return this; } /** *

Registers a parameter converter class that convert strings to and from a custom class.

*

This allows you to specify query string parameters, form values, header params and path params as custom classes.

*

For more functionality, {@link #addCustomParamConverterProvider(ParamConverterProvider)} is also available.

* @param paramClass The class that this converter is meant for. * @param converter The converter * @param

The type of the parameter * @return This builder */ public

RestHandlerBuilder addCustomParamConverter(Class

paramClass, ParamConverter

converter) { return addCustomParamConverterProvider(new ParamConverterProvider() { @Override public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) { if (!rawType.equals(paramClass)) { return null; } return (ParamConverter) converter; } }); } /** * Enables an Open API JSON URL at the specified endpoint. This JSON describes the API exposed * by the rest resources declared by this builder, and can be used by UIs such as Swagger. * * @param url The URL to serve from, for example /openapi.json or null to disable the JSON endpoint. Disabled by default. * @return The current Rest Handler Builder * @see #withOpenApiDocument(OpenAPIObjectBuilder) * @see #withOpenApiHtmlUrl(String) */ public RestHandlerBuilder withOpenApiJsonUrl(String url) { this.openApiJsonUrl = url; return this; } /** * Enables a simple HTML endpoint that documents the API exposed by the rest resources declared by this builder. * * @param url The URL to serve from, for example /api.html or null to disable the HTML endpoint. Disabled by default. * @return The current Rest Handler Builder * @see #withOpenApiDocument(OpenAPIObjectBuilder) * @see #withOpenApiJsonUrl(String) * @see #withOpenApiHtmlCss(String) */ public RestHandlerBuilder withOpenApiHtmlUrl(String url) { this.openApiHtmlUrl = url; return this; } /** * When using the HTML endpoint made available by calling {@link #withOpenApiDocument(OpenAPIObjectBuilder)} * this allows you to override the default CSS that is used. * * @param css A string containing a style sheet definition. * @return The current Rest Handler Builder */ public RestHandlerBuilder withOpenApiHtmlCss(String css) { this.openApiHtmlCss = css; return this; } /** *

Use this value to create JSON and HTML documentation for your rest service.

*

Minimal example:

*

     *     OpenAPIObjectBuilder.openAPIObject()
     *             .withInfo(InfoObjectBuilder.infoObject()
     *                 .withTitle("Mu Server Sample API")
     *                 .withVersion("1.0")
     *                 .build())
     * 
*

Extended example:

*

     *     OpenAPIObjectBuilder.openAPIObject()
     *             .withInfo(InfoObjectBuilder.infoObject()
     *                 .withTitle("Mu Server Sample API")
     *                 .withVersion("1.0")
     *                 .withLicense(LicenseObjectBuilder.Apache2_0())
     *                 .withDescription("This is the **description**\n\nWhich is markdown")
     *                 .withTermsOfService(URI.create("http://example.org/terms/"))
     *                 .build())
     *             .withExternalDocs(externalDocumentationObject()
     *                 .withDescription("Full documentation")
     *                 .withUrl(URI.create("http://example.org/docs"))
     *                 .build())
     * 
*

The path information and operation information will be automatically generated. By default, you can access * the Open API specification of your rest service at /openapi.json or view the HTML at * /api.html

* * @param openAPIObject An API Object builder with the {@link OpenAPIObjectBuilder#withInfo(InfoObject)} set. * @return The current Rest Handler Builder * @see OpenAPIObjectBuilder#openAPIObject() * @see #withOpenApiJsonUrl(String) * @see #withOpenApiHtmlUrl(String) */ public RestHandlerBuilder withOpenApiDocument(OpenAPIObjectBuilder openAPIObject) { this.openAPIObject = openAPIObject; return this; } /** *

Adds a mapper that converts an exception to a response.

*

For example, you may create a custom exception such as a ValidationException that you throw from your * jax-rs methods. A mapper for this exception type could return a Response with a 400 code and a custom * validation error message.

* @param The exception type that the mapper can handle * @param exceptionClass The type of exception to map. * @param exceptionMapper A function that creates a {@link javax.ws.rs.core.Response} suitable for the exception. * @return Returns this builder. */ public RestHandlerBuilder addExceptionMapper(Class exceptionClass, ExceptionMapper exceptionMapper) { this.exceptionMappers.put(exceptionClass, exceptionMapper); return this; } /** * @return The current Rest Handler Builder * @deprecated This does nothing. To expose API endpoints, use {@link #withOpenApiJsonUrl(String)} and/or {@link #withOpenApiHtmlUrl(String)} */ @Deprecated public RestHandlerBuilder withDocumentation() { return this; } public RestHandler build() { List readers = EntityProviders.builtInReaders(); readers.addAll(customReaders); List writers = EntityProviders.builtInWriters(); writers.addAll(customWriters); EntityProviders entityProviders = new EntityProviders(readers, writers); List paramConverterProviders = new ArrayList<>(customParamConverterProviders); paramConverterProviders.add(new BuiltInParamConverterProvider()); HashSet set = new HashSet<>(); for (Object restResource : resources) { set.add(ResourceClass.fromObject(restResource, paramConverterProviders)); } Set roots = Collections.unmodifiableSet(set); OpenApiDocumentor documentor = null; if (openApiHtmlUrl != null || openApiJsonUrl != null) { if (openApiHtmlCss == null) { InputStream cssStream = RestHandlerBuilder.class.getResourceAsStream("/io/muserver/resources/api.css"); openApiHtmlCss = new Scanner(cssStream, "UTF-8").useDelimiter("\\A").next(); } OpenAPIObjectBuilder openAPIObjectToUse = this.openAPIObject == null ? OpenAPIObjectBuilder.openAPIObject() : this.openAPIObject; openAPIObjectToUse.withPaths(pathsObject().build()); documentor = new OpenApiDocumentor(roots, openApiJsonUrl, openApiHtmlUrl, openAPIObjectToUse.build(), openApiHtmlCss, corsConfig); } CustomExceptionMapper customExceptionMapper = new CustomExceptionMapper(exceptionMappers); FilterManagerThing filterManagerThing = new FilterManagerThing(preMatchRequestFilters, requestFilters, responseFilters); return new RestHandler(entityProviders, roots, documentor, customExceptionMapper, filterManagerThing, corsConfig); } /** *

Creates a handler builder for JAX-RS REST services.

*

Note that CORS is disabled by default.

* @param resources Instances of classes that have a {@link javax.ws.rs.Path} annotation. * @return Returns a builder that can be used to specify more config */ public static RestHandlerBuilder restHandler(Object... resources) { return new RestHandlerBuilder(resources); } /** *

Specifies the CORS config for the REST services. Defaults to {@link CORSConfigBuilder#disabled()}

* @see CORSConfigBuilder * @param corsConfig The CORS config to use * @return This builder. */ public RestHandlerBuilder withCORS(CORSConfig corsConfig) { this.corsConfig = corsConfig; return this; } /** *

Specifies the CORS config for the REST services. Defaults to {@link CORSConfigBuilder#disabled()}

* @see CORSConfigBuilder * @param corsConfig The CORS config to use * @return This builder. */ public RestHandlerBuilder withCORS(CORSConfigBuilder corsConfig) { return withCORS(corsConfig.build()); } /** * @param resources Resources to register * @return Returns a rest handler with the given resources. * @deprecated Use restHandler(resources).build() instead. */ @Deprecated public static RestHandler create(Object... resources) { return restHandler(resources).build(); } /** *

Registers a request filter, which is run before a rest method is executed.

*

It will be run after the method has been matched, or if the {@link PreMatching} annotation is applied to the * filter then it will run before matching occurs.

* @param filter The filter to register * @return This builder */ public RestHandlerBuilder addRequestFilter(ContainerRequestFilter filter) { if (filter.getClass().getDeclaredAnnotation(PreMatching.class) != null) { this.preMatchRequestFilters.add(filter); } else { this.requestFilters.add(filter); } return this; } /** * Registers a response filter, which is called after execution of a method takes place. * @param filter The filter to register * @return This builder */ public RestHandlerBuilder addResponseFilter(ContainerResponseFilter filter) { this.responseFilters.add(filter); return this; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy