This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 58db5a9 REST API refactoring.
58db5a9 is described below
commit 58db5a98ac7103643f6587337722870500db6368
Author: JamesBognar <[email protected]>
AuthorDate: Tue Jan 26 09:59:38 2021 -0500
REST API refactoring.
---
.../java/org/apache/juneau/rest/RestContext.java | 663 +++++++---
.../org/apache/juneau/rest/SwaggerProvider.java | 1320 ++++++++++++++++++++
.../apache/juneau/rest/SwaggerProviderBuilder.java | 115 ++
3 files changed, 1958 insertions(+), 140 deletions(-)
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index d70c08e..f3a5e06 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -18,8 +18,10 @@ import static org.apache.juneau.internal.ObjectUtils.*;
import static org.apache.juneau.internal.IOUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
import static org.apache.juneau.rest.HttpRuntimeException.*;
+import static org.apache.juneau.rest.logging.RestLoggingDetail.*;
import static org.apache.juneau.Enablement.*;
import static java.util.Collections.*;
+import static java.util.logging.Level.*;
import static java.util.Arrays.*;
import java.io.*;
@@ -63,6 +65,7 @@ import org.apache.juneau.rest.converters.*;
import org.apache.juneau.rest.logging.*;
import org.apache.juneau.rest.params.*;
import org.apache.juneau.http.exception.*;
+import org.apache.juneau.http.header.*;
import org.apache.juneau.rest.reshandlers.*;
import org.apache.juneau.rest.util.*;
import org.apache.juneau.rest.vars.*;
@@ -2893,6 +2896,34 @@ public class RestContext extends BeanContext {
public static final String REST_staticFilesDefault = PREFIX +
".staticFilesDefault.o";
/**
+ * Configuration property: Swagger provider class.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul class='spaced-list'>
+ * <li><b>ID:</b> {@link
org.apache.juneau.rest.RestContext#REST_swaggerProviderClass
REST_swaggerProviderClass}
+ * <li><b>Name:</b> <js>"RestContext.swaggerProviderClass.c"</js>
+ * <li><b>Data type:</b> {@link
org.apache.juneau.rest.SwaggerProvider}
+ * <li><b>Default:</b> {@link
org.apache.juneau.rest.SwaggerProvider}
+ * <li><b>Session property:</b> <jk>false</jk>
+ * <li><b>Annotations:</b>
+ * <ul>
+ * <li class='ja'>{@link
org.apache.juneau.rest.annotation.Rest#infoProvider()}
+ * </ul>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link
org.apache.juneau.rest.RestContextBuilder#infoProvider(Class)}
+ * <li class='jm'>{@link
org.apache.juneau.rest.RestContextBuilder#infoProvider(RestInfoProvider)}
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * The default static file finder.
+ * <p>
+ * This setting is inherited from the parent context.
+ */
+ public static final String REST_swaggerProviderClass = PREFIX +
".swaggerProviderClass.c";
+
+ /**
* Configuration property: Supported accept media types.
*
* <h5 class='section'>Property:</h5>
@@ -3421,6 +3452,7 @@ public class RestContext extends BeanContext {
private final StackTraceStore stackTraceStore;
private final Logger logger;
private final RestInfoProvider infoProvider;
+ private final SwaggerProvider swaggerProvider;
private final HttpException initException;
private final RestContext parentContext;
final BeanFactory rootBeanFactory;
@@ -3503,9 +3535,9 @@ public class RestContext extends BeanContext {
parentContext = builder.parentContext;
ClassInfo rci = ClassInfo.ofProxy(r);
- rootBeanFactory = createRootBeanFactory(r);
+ rootBeanFactory = createBeanFactory(r);
- beanFactory = createBeanFactory(r);
+ beanFactory = BeanFactory.of(rootBeanFactory, r);
beanFactory.addBean(BeanFactory.class, beanFactory);
beanFactory.addBean(RestContext.class, this);
beanFactory.addBean(Object.class, r);
@@ -3597,10 +3629,11 @@ public class RestContext extends BeanContext {
preCallMethods =
createPreCallMethods(r).stream().map(this::toRestMethodInvoker).toArray(RestMethodInvoker[]::
new);
postCallMethods =
createPostCallMethods(r).stream().map(this::toRestMethodInvoker).toArray(RestMethodInvoker[]::
new);
- restMethods = createRestMethods(r).build();
- restChildren = createRestChildren(r).build();
+ restMethods = createRestMethods(r);
+ restChildren = createRestChildren(r);
infoProvider = createInfoProvider(r, beanFactory);
+ swaggerProvider = createSwaggerProvider(r, beanFactory);
} catch (HttpException e) {
_initException = e;
@@ -3622,9 +3655,85 @@ public class RestContext extends BeanContext {
}
/**
+ * Instantiates the bean factory for this REST resource.
+ *
+ * <p>
+ * The bean factory is typically used for passing in injected beans
into REST contexts and for storing beans
+ * created by the REST context.
+ *
+ * <p>
+ * Instantiates based on the following logic:
+ * <ul>
+ * <li>Returns the resource class itself if it's an instance of
{@link BeanFactory}.
+ * <li>Looks for {@link #REST_beanFactory} value set via any of
the following:
+ * <ul>
+ * <li>{@link
RestContextBuilder#beanFactory(Class)}/{@link
RestContextBuilder#beanFactory(BeanFactory)}
+ * <li>{@link Rest#beanFactory()}.
+ * </ul>
+ * <li>Instantiates a new {@link BeanFactory}.
+ * Uses the parent context's root bean factory as the
parent bean factory if this is a child resource.
+ * </ul>
+ *
+ * <p>
+ * Your REST class can also implement a create method called
<c>createBeanFactory()</c> to instantiate your own
+ * bean factory.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bpcode w800'>
+ * <ja>@Rest</ja>
+ * <jk>public class</jk> MyRestClass {
+ *
+ * <jk>public</jk> BeanFactory
createBeanFactory(Optional<BeanFactory> <jv>parentBeanFactory</jv>)
<jk>throws</jk> Exception {
+ * <jc>// Create your own bean factory here.</jc>
+ * }
+ * }
+ * </p>
+ *
+ * <p>
+ * The <c>createBeanFactory()</c> method can be static or non-static
can contain any of the following arguments:
+ * <ul>
+ * <li><c>{@link Optional}<{@link BeanFactory}></c> - The
parent root bean factory if this is a child resource.
+ * </ul>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_beanFactory}
+ * </ul>
+ *
+ * @param resource The REST resource object.
+ * @return The bean factory for this REST resource.
+ * @throws Exception If bean factory could not be instantiated.
+ */
+ protected BeanFactory createBeanFactory(Object resource) throws
Exception {
+
+ BeanFactory x = null;
+
+ if (resource instanceof BeanFactory)
+ x = (BeanFactory)resource;
+
+ if (x == null && parentContext != null)
+ x = parentContext.rootBeanFactory;
+
+ if (x == null)
+ x = getInstanceProperty(REST_beanFactory,
BeanFactory.class, null, x);
+
+ x = BeanFactory
+ .of(x, resource)
+ .addBean(BeanFactory.class, x)
+ .beanCreateMethodFinder(BeanFactory.class, resource)
+ .find("createBeanFactory")
+ .withDefault(x)
+ .run();
+
+ return x;
+ }
+
+ /**
* Instantiates the file finder for this REST resource.
*
* <p>
+ * The file finder is used to retrieve localized files from the
classpath.
+ *
+ * <p>
* Instantiates based on the following logic:
* <ul>
* <li>Returns the resource class itself is an instance of {@link
FileFinder}.
@@ -3633,23 +3742,44 @@ public class RestContext extends BeanContext {
* <li>{@link
RestContextBuilder#fileFinder(Class)}/{@link
RestContextBuilder#fileFinder(FileFinder)}
* <li>{@link Rest#fileFinder()}.
* </ul>
- * <li>Looks for a static or non-static <c>createFileFinder()</>
method that returns {@link FileFinder} on the
- * resource class with any of the following arguments:
- * <ul>
- * <li>{@link RestContext}
- * <li>{@link BeanFactory}
- * <li>Any {@doc RestInjection injected beans}.
- * </ul>
- * <li>Resolves it via the bean factory registered in this context
(including any Spring beans).
+ * <li>Resolves it via the {@link #createBeanFactory(Object) bean
factory} registered in this context (including Spring beans if using
SpringRestServlet).
* <li>Looks for value in {@link #REST_fileFinderDefault} setting.
- * <li>Instantiates a {@link BasicFileFinder}.
+ * <li>Instantiates via {@link #createFileFinderBuilder(Object,
BeanFactory)}.
+ * </ul>
+ *
+ * <p>
+ * Your REST class can also implement a create method called
<c>createFileFinder()</c> to instantiate your own
+ * file finder.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bpcode w800'>
+ * <ja>@Rest</ja>
+ * <jk>public class</jk> MyRestClass {
+ *
+ * <jk>public</jk> FileFinder createFileFinder()
<jk>throws</jk> Exception {
+ * <jc>// Create your own file finder here.</jc>
+ * }
+ * }
+ * </p>
+ *
+ * <p>
+ * The <c>createFileFinder()</c> method can be static or non-static can
contain any of the following arguments:
+ * <ul>
+ * <li>{@link FileFinder} - The file finder that would have been
returned by this method.
+ * <li>{@link FileFinderBuilder} - The file finder returned by
{@link #createFileFinderBuilder(Object,BeanFactory)}.
+ * <li>{@link RestContext} - This REST context.
+ * <li>{@link BeanFactory} - The bean factory of this REST context.
+ * <li>Any {@doc RestInjection injected bean} types. Use {@link
Optional} arguments for beans that may not exist.
+ * </ul>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_fileFinder}
* </ul>
*
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @return The file finder for this REST resource.
* @throws Exception If file finder could not be instantiated.
- * @seealso #REST_fileFinder
*/
protected FileFinder createFileFinder(Object resource, BeanFactory
beanFactory) throws Exception {
@@ -3668,7 +3798,7 @@ public class RestContext extends BeanContext {
x = getInstanceProperty(REST_fileFinderDefault,
FileFinder.class, null, beanFactory);
if (x == null)
- x = new BasicFileFinder(this);
+ x = createFileFinderBuilder(resource,
beanFactory).build();
x = BeanFactory
.of(beanFactory, resource)
@@ -3682,6 +3812,39 @@ public class RestContext extends BeanContext {
}
/**
+ * Instantiates the file finder builder for this REST resource.
+ *
+ * <p>
+ * Allows subclasses to intercept and modify the builder used by the
{@link #createFileFinder(Object, BeanFactory)} method.
+ *
+ * @param resource The REST resource object.
+ * @param beanFactory The bean factory to use for retrieving and
creating beans.
+ * @return The file finder builder for this REST resource.
+ * @throws Exception If file finder builder could not be instantiated.
+ */
+ protected FileFinderBuilder createFileFinderBuilder(Object resource,
BeanFactory beanFactory) throws Exception {
+
+ FileFinderBuilder x = FileFinder
+ .create()
+ .dir("static")
+ .dir("htdocs")
+ .cp(getResourceClass(), "htdocs", true)
+ .cp(getResourceClass(), "/htdocs", true)
+ .caching(1_000_000)
+ .exclude("(?i).*\\.(class|properties)");
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(FileFinderBuilder.class, x)
+ .beanCreateMethodFinder(FileFinderBuilder.class,
resource)
+ .find("createFileFinder")
+ .withDefault(x)
+ .run();
+
+ return x;
+ }
+
+ /**
* Instantiates the REST info provider for this REST resource.
*
* <p>
@@ -3704,11 +3867,14 @@ public class RestContext extends BeanContext {
* <li>Instantiates a {@link BasicRestInfoProvider}.
* </ul>
*
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_infoProvider}
+ * </ul>
+ *
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @return The info provider for this REST resource.
* @throws Exception If info provider could not be instantiated.
- * @seealso #REST_infoProvider
*/
protected RestInfoProvider createInfoProvider(Object resource,
BeanFactory beanFactory) throws Exception {
@@ -3762,11 +3928,14 @@ public class RestContext extends BeanContext {
* <li>Instantiates a {@link BasicStaticFiles}.
* </ul>
*
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_staticFiles}
+ * </ul>
+ *
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @return The file finder for this REST resource.
* @throws Exception If file finder could not be instantiated.
- * @seealso #REST_staticFiles
*/
protected StaticFiles createStaticFiles(Object resource, BeanFactory
beanFactory) throws Exception {
@@ -3785,7 +3954,7 @@ public class RestContext extends BeanContext {
x = getInstanceProperty(REST_staticFilesDefault,
StaticFiles.class, null, beanFactory);
if (x == null)
- x = new BasicStaticFiles(this);
+ x = createStaticFilesBuilder(resource,
beanFactory).build();
x = BeanFactory
.of(beanFactory, resource)
@@ -3799,6 +3968,40 @@ public class RestContext extends BeanContext {
}
/**
+ * Instantiates the static files builder for this REST resource.
+ *
+ * <p>
+ * Allows subclasses to intercept and modify the builder used by the
{@link #createStaticFiles(Object, BeanFactory)} method.
+ *
+ * @param resource The REST resource object.
+ * @param beanFactory The bean factory to use for retrieving and
creating beans.
+ * @return The static files builder for this REST resource.
+ * @throws Exception If static files builder could not be instantiated.
+ */
+ protected StaticFilesBuilder createStaticFilesBuilder(Object resource,
BeanFactory beanFactory) throws Exception {
+
+ StaticFilesBuilder x = StaticFiles
+ .create()
+ .dir("static")
+ .dir("htdocs")
+ .cp(getResourceClass(), "htdocs", true)
+ .cp(getResourceClass(), "/htdocs", true)
+ .caching(1_000_000)
+ .exclude("(?i).*\\.(class|properties)")
+ .headers(CacheControl.of("max-age=86400, public"));
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(StaticFilesBuilder.class, x)
+ .beanCreateMethodFinder(StaticFilesBuilder.class,
resource)
+ .find("createStaticFiles")
+ .withDefault(x)
+ .run();
+
+ return x;
+ }
+
+ /**
* Instantiates the call logger this REST resource.
*
* <p>
@@ -3823,11 +4026,14 @@ public class RestContext extends BeanContext {
* <li>Instantiates a {@link BasicFileFinder}.
* </ul>
*
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_callLogger}
+ * </ul>
+ *
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @return The file finder for this REST resource.
* @throws Exception If file finder could not be instantiated.
- * @seealso #REST_callLogger
*/
protected RestLogger createCallLogger(Object resource, BeanFactory
beanFactory) throws Exception {
@@ -3846,7 +4052,7 @@ public class RestContext extends BeanContext {
x = getInstanceProperty(REST_callLoggerDefault,
RestLogger.class, null, beanFactory);
if (x == null)
- x = new BasicRestLogger(this);
+ x = createCallLoggerBuilder(resource,
beanFactory).build();
x = BeanFactory
.of(beanFactory, resource)
@@ -3860,112 +4066,49 @@ public class RestContext extends BeanContext {
}
/**
- * Instantiates the bean factory for this REST resource.
+ * Instantiates the call logger builder for this REST resource.
*
* <p>
- * Instantiates based on the following logic:
- * <ul>
- * <li>Returns the resource class itself is an instance of {@link
BeanFactory}.
- * <li>Looks for {@link #REST_beanFactory} value set via any of
the following:
- * <ul>
- * <li>{@link
RestContextBuilder#beanFactory(Class)}/{@link
RestContextBuilder#beanFactory(BeanFactory)}
- * <li>{@link Rest#beanFactory()}.
- * </ul>
- * <li>Looks for a static or non-static <c>beanFactory()</> method
that returns {@link BeanFactory} on the
- * resource class with any of the following arguments:
- * <ul>
- * <li>{@link RestContext}
- * <li>{@link BeanFactory} - The parent resource
bean factory if this is a child.
- * <li>Any {@doc RestInjection injected beans}.
- * </ul>
- * <li>Resolves it via the bean factory registered in this context.
- * <li>Instantiates a {@link BeanFactory}.
- * </ul>
+ * Allows subclasses to intercept and modify the builder used by the
{@link #createCallLogger(Object, BeanFactory)} method.
*
* @param resource The REST resource object.
- * @return The bean factory for this REST resource.
- * @throws Exception If bean factory could not be instantiated.
- * @seealso #REST_beanFactory
- */
- protected BeanFactory createBeanFactory(Object resource) throws
Exception {
-
- BeanFactory x = null;
-
- if (resource instanceof BeanFactory)
- x = (BeanFactory)resource;
-
- BeanFactory bf = createRootBeanFactory(resource)
- .addBean(RestContext.class, this)
- .addBean(BeanFactory.class, parentContext == null ?
null : parentContext.rootBeanFactory)
- .addBean(PropertyStore.class, getPropertyStore())
- .addBean(Object.class, resource);
-
- if (x == null)
- x = getInstanceProperty(REST_beanFactory,
BeanFactory.class, null, bf);
-
- if (x == null)
- x = bf;
-
- x = bf
- .beanCreateMethodFinder(BeanFactory.class, resource)
- .find("createBeanFactory")
- .withDefault(x)
- .run();
-
- return x;
- }
-
- /**
- * Instantiates the root bean factory for this REST resource.
- *
- * <p>
- * The root bean factory is the factory used for passing in injected
beans.
- * Beans created by this context are not added to this factory.
- *
- * <p>
- * Instantiates based on the following logic:
- * <ul>
- * <li>Returns the resource class itself is an instance of {@link
BeanFactory}.
- * <li>Looks for {@link #REST_beanFactory} value set via any of
the following:
- * <ul>
- * <li>{@link
RestContextBuilder#beanFactory(Class)}/{@link
RestContextBuilder#beanFactory(BeanFactory)}
- * <li>{@link Rest#beanFactory()}.
- * </ul>
- * <li>Looks for a static or non-static <c>beanFactory()</> method
that returns {@link BeanFactory} on the
- * resource class with any of the following arguments:
- * <ul>
- * <li>{@link RestContext}
- * <li>{@link BeanFactory} - The parent resource
bean factory if this is a child.
- * </ul>
- * <li>Resolves it via the bean factory registered in this context.
- * <li>Instantiates a {@link BeanFactory}.
- * </ul>
- *
- * @param resource The REST resource object.
- * @return The bean factory for this REST resource.
- * @throws Exception If bean factory could not be instantiated.
- * @seealso #REST_beanFactory
+ * @param beanFactory The bean factory to use for retrieving and
creating beans.
+ * @return The call logger builder for this REST resource.
+ * @throws Exception If call logger builder could not be instantiated.
*/
- protected BeanFactory createRootBeanFactory(Object resource) throws
Exception {
-
- BeanFactory x = null;
-
- if (resource instanceof BeanFactory)
- x = (BeanFactory)resource;
-
- BeanFactory parent = parentContext == null ? null :
parentContext.rootBeanFactory;
- BeanFactory bf = BeanFactory.of(parent, resource);
- bf.addBean(BeanFactory.class, bf);
-
- if (x == null)
- x = getInstanceProperty(REST_beanFactory,
BeanFactory.class, null, bf);
+ protected RestLoggerBuilder createCallLoggerBuilder(Object resource,
BeanFactory beanFactory) throws Exception {
- if (x == null)
- x = bf;
+ RestLoggerBuilder x = RestLogger
+ .create()
+ .normalRules( // Rules when debugging is not enabled.
+ RestLogger.createRule() // Log 500+ errors
with status-line and header information.
+ .statusFilter(a -> a >= 500)
+ .level(SEVERE)
+ .requestDetail(HEADER)
+ .responseDetail(HEADER)
+ .build(),
+ RestLogger.createRule() // Log 400-500 errors
with just status-line information.
+ .statusFilter(a -> a >= 400)
+ .level(WARNING)
+ .requestDetail(STATUS_LINE)
+ .responseDetail(STATUS_LINE)
+ .build()
+ )
+ .debugRules( // Rules when debugging is enabled.
+ RestLogger.createRule() // Log everything with
full details.
+ .level(SEVERE)
+ .requestDetail(ENTITY)
+ .responseDetail(ENTITY)
+ .build()
+ )
+ .logger(getLogger())
+ .stackTraceStore(getStackTraceStore());
- x = bf
- .beanCreateMethodFinder(BeanFactory.class, resource)
- .find("createBeanFactory")
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(RestLoggerBuilder.class, x)
+ .beanCreateMethodFinder(RestLoggerBuilder.class,
resource)
+ .find("createCallLogger")
.withDefault(x)
.run();
@@ -3994,11 +4137,14 @@ public class RestContext extends BeanContext {
* <li>Instantiates a <c>ResponseHandler[0]</c>.
* </ul>
*
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_responseHandlers}
+ * </ul>
+ *
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @return The response handlers for this REST resource.
* @throws Exception If response handlers could not be instantiated.
- * @seealso #REST_responseHandlers
*/
protected ResponseHandlerList createResponseHandlers(Object resource,
BeanFactory beanFactory) throws Exception {
@@ -4042,12 +4188,15 @@ public class RestContext extends BeanContext {
* <li>Instantiates a <c>Serializer[0]</c>.
* </ul>
*
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_serializers}
+ * </ul>
+ *
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @param ps The property store to apply to all serialiers.
* @return The serializers for this REST resource.
* @throws Exception If serializers could not be instantiated.
- * @seealso #REST_serializers
*/
protected SerializerGroup createSerializers(Object resource,
BeanFactory beanFactory, PropertyStore ps) throws Exception {
@@ -4102,12 +4251,15 @@ public class RestContext extends BeanContext {
* <li>Instantiates a <c>Parser[0]</c>.
* </ul>
*
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_parsers}
+ * </ul>
+ *
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @param ps The property store to apply to all serialiers.
* @return The parsers for this REST resource.
* @throws Exception If parsers could not be instantiated.
- * @seealso #REST_parsers
*/
protected ParserGroup createParsers(Object resource, BeanFactory
beanFactory, PropertyStore ps) throws Exception {
@@ -4163,11 +4315,14 @@ public class RestContext extends BeanContext {
* <li>Instantiates an {@link OpenApiSerializer}.
* </ul>
*
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_partSerializer}
+ * </ul>
+ *
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @return The HTTP part serializer for this REST resource.
* @throws Exception If serializer could not be instantiated.
- * @seealso #REST_partSerializer
*/
protected HttpPartSerializer createPartSerializer(Object resource,
BeanFactory beanFactory) throws Exception {
@@ -4219,11 +4374,14 @@ public class RestContext extends BeanContext {
* <li>Instantiates an {@link OpenApiSerializer}.
* </ul>
*
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_partParser}
+ * </ul>
+ *
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @return The HTTP part parser for this REST resource.
* @throws Exception If parser could not be instantiated.
- * @seealso #REST_partParser
*/
protected HttpPartParser createPartParser(Object resource, BeanFactory
beanFactory) throws Exception {
@@ -4272,7 +4430,6 @@ public class RestContext extends BeanContext {
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @return The REST method parameter resolvers for this REST resource.
* @throws Exception If parameter resolvers could not be instantiated.
- * @seealso #REST_paramResolvers
*/
@SuppressWarnings("unchecked")
protected RestParamList createRestParams(Object resource, BeanFactory
beanFactory) throws Exception {
@@ -4344,7 +4501,6 @@ public class RestContext extends BeanContext {
* @param beanFactory The bean factory to use for retrieving and
creating beans.
* @return The REST method parameter resolvers for this REST resource.
* @throws Exception If parameter resolvers could not be instantiated.
- * @seealso #REST_paramResolvers
*/
@SuppressWarnings("unchecked")
protected RestParamList createHookMethodParams(Object resource,
BeanFactory beanFactory) throws Exception {
@@ -4446,14 +4602,10 @@ public class RestContext extends BeanContext {
* @throws Exception If JSON schema generator could not be instantiated.
*/
protected JsonSchemaGenerator createJsonSchemaGenerator(Object
resource, BeanFactory beanFactory) throws Exception {
-
JsonSchemaGenerator x =
beanFactory.getBean(JsonSchemaGenerator.class).orElse(null);
if (x == null)
- x = JsonSchemaGenerator
- .create()
- .apply(getPropertyStore())
- .build();
+ x = createJsonSchemaGeneratorBuilder(resource,
beanFactory).build();
x = BeanFactory
.of(beanFactory, resource)
@@ -4467,6 +4619,116 @@ public class RestContext extends BeanContext {
}
/**
+ * Instantiates the JSON-schema generator builder for this REST
resource.
+ *
+ * <p>
+ * Allows subclasses to intercept and modify the builder used by the
{@link #createJsonSchemaGenerator(Object, BeanFactory)} method.
+ *
+ * @param resource The REST resource object.
+ * @param beanFactory The bean factory to use for retrieving and
creating beans.
+ * @return The JSON-schema generator builder for this REST resource.
+ * @throws Exception If JSON-schema generator builder could not be
instantiated.
+ */
+ protected JsonSchemaGeneratorBuilder
createJsonSchemaGeneratorBuilder(Object resource, BeanFactory beanFactory)
throws Exception {
+ JsonSchemaGeneratorBuilder x = JsonSchemaGenerator
+ .create()
+ .apply(getPropertyStore());
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(JsonSchemaGeneratorBuilder.class, x)
+
.beanCreateMethodFinder(JsonSchemaGeneratorBuilder.class, resource)
+ .find("createJsonSchemaGenerator")
+ .withDefault(x)
+ .run();
+
+ return x;
+ }
+
+ /**
+ * Instantiates the REST info provider for this REST resource.
+ *
+ * <p>
+ * Instantiates based on the following logic:
+ * <ul>
+ * <li>Returns the resource class itself is an instance of {@link
RestInfoProvider}.
+ * <li>Looks for {@link #REST_infoProvider} value set via any of
the following:
+ * <ul>
+ * <li>{@link
RestContextBuilder#infoProvider(Class)}/{@link
RestContextBuilder#infoProvider(RestInfoProvider)}
+ * <li>{@link Rest#infoProvider()}.
+ * </ul>
+ * <li>Looks for a static or non-static <c>createInfoProvider()</>
method that returns {@link RestInfoProvider} on the
+ * resource class with any of the following arguments:
+ * <ul>
+ * <li>{@link RestContext}
+ * <li>{@link BeanFactory}
+ * <li>Any {@doc RestInjection injected beans}.
+ * </ul>
+ * <li>Resolves it via the bean factory registered in this context.
+ * <li>Instantiates a {@link BasicRestInfoProvider}.
+ * </ul>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link #REST_infoProvider}
+ * </ul>
+ *
+ * @param resource The REST resource object.
+ * @param beanFactory The bean factory to use for retrieving and
creating beans.
+ * @return The info provider for this REST resource.
+ * @throws Exception If info provider could not be instantiated.
+ */
+ protected SwaggerProvider createSwaggerProvider(Object resource,
BeanFactory beanFactory) throws Exception {
+ SwaggerProvider x =
beanFactory.getBean(SwaggerProvider.class).orElse(null);
+
+ if (x == null)
+ x = createSwaggerProviderBuilder(resource,
beanFactory).build();
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(SwaggerProvider.class, x)
+ .beanCreateMethodFinder(SwaggerProvider.class, resource)
+ .find("createSwaggerProvider")
+ .withDefault(x)
+ .run();
+
+ return x;
+ }
+
+ /**
+ * Instantiates the REST API builder for this REST resource.
+ *
+ * <p>
+ * Allows subclasses to intercept and modify the builder used by the
{@link #createSwaggerProvider(Object, BeanFactory)} method.
+ *
+ * @param resource The REST resource object.
+ * @param beanFactory The bean factory to use for retrieving and
creating beans.
+ * @return The REST API builder for this REST resource.
+ * @throws Exception If REST API builder could not be instantiated.
+ */
+ protected SwaggerProviderBuilder createSwaggerProviderBuilder(Object
resource, BeanFactory beanFactory) throws Exception {
+
+ SwaggerProviderBuilder x = SwaggerProvider
+ .create()
+ .beanFactory(beanFactory)
+ .fileFinder(getFileFinder())
+ .messages(getMessages())
+ .varResolver(getVarResolver())
+
.jsonSchemaGenerator(createJsonSchemaGenerator(resource, beanFactory))
+
.implClass(getClassProperty(REST_swaggerProviderClass, SwaggerProvider.class,
SwaggerProvider.class));
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(SwaggerProviderBuilder.class, x)
+ .beanCreateMethodFinder(SwaggerProviderBuilder.class,
resource)
+ .find("createSwaggerProvider")
+ .withDefault(x)
+ .run();
+
+ return x;
+
+ }
+
+ /**
* Instantiates the variable resolver for this REST resource.
*
* <p>
@@ -4723,21 +4985,58 @@ public class RestContext extends BeanContext {
* @throws Exception An error occurred.
*/
protected Messages createMessages(Object resource) throws Exception {
+
+ Messages x = createMessagesBuilder(resource).build();
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(Messages.class, x)
+ .beanCreateMethodFinder(Messages.class, resource)
+ .find("createMessages")
+ .withDefault(x)
+ .run();
+
+ return x;
+ }
+
+ /**
+ * Instantiates the Messages builder for this REST resource.
+ *
+ * <p>
+ * Allows subclasses to intercept and modify the builder used by the
{@link #createMessages(Object)} method.
+ *
+ * @param resource The REST resource object.
+ * @return The messages builder for this REST resource.
+ * @throws Exception If messages builder could not be instantiated.
+ */
+ protected MessagesBuilder createMessagesBuilder(Object resource) throws
Exception {
+
Tuple2<Class<?>,String>[] mbl =
getInstanceArrayProperty(REST_messages, Tuple2.class);
- Messages msgs = null;
+ MessagesBuilder x = null;
+
for (int i = mbl.length-1; i >= 0; i--) {
Class<?> c = firstNonNull(mbl[i].getA(),
resource.getClass());
String value = mbl[i].getB();
if (isJsonObject(value,true)) {
- MessagesString x =
SimpleJson.DEFAULT.read(value, MessagesString.class);
- msgs =
Messages.create(c).name(x.name).baseNames(split(x.baseNames,
',')).locale(x.locale).parent(msgs).build();
+ MessagesString ms =
SimpleJson.DEFAULT.read(value, MessagesString.class);
+ x =
Messages.create(c).name(ms.name).baseNames(split(ms.baseNames,
',')).locale(ms.locale).parent(x == null ? null : x.build());
} else {
- msgs =
Messages.create(c).name(value).parent(msgs).build();
+ x = Messages.create(c).name(value).parent(x ==
null ? null : x.build());
}
}
- if (msgs == null)
- msgs = Messages.create(resource.getClass()).build();
- return msgs;
+
+ if (x == null)
+ x = Messages.create(resource.getClass());
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(MessagesBuilder.class, x)
+ .beanCreateMethodFinder(MessagesBuilder.class, resource)
+ .find("createMessages")
+ .withDefault(x)
+ .run();
+
+ return x;
}
private static class MessagesString {
@@ -4753,7 +5052,33 @@ public class RestContext extends BeanContext {
* @return The builder for the {@link RestMethods} object.
* @throws Exception An error occurred.
*/
- protected RestMethodsBuilder createRestMethods(Object resource) throws
Exception {
+ protected RestMethods createRestMethods(Object resource) throws
Exception {
+
+ RestMethods x = createRestMethodsBuilder(resource).build();
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(RestMethods.class, x)
+ .beanCreateMethodFinder(RestMethods.class, resource)
+ .find("createRestMethods")
+ .withDefault(x)
+ .run();
+
+ return x;
+ }
+
+ /**
+ * Instantiates the REST methods builder for this REST resource.
+ *
+ * <p>
+ * Allows subclasses to intercept and modify the builder used by the
{@link #createRestMethods(Object)} method.
+ *
+ * @param resource The REST resource object.
+ * @return The REST methods builder for this REST resource.
+ * @throws Exception If REST methods builder could not be instantiated.
+ */
+ protected RestMethodsBuilder createRestMethodsBuilder(Object resource)
throws Exception {
+
RestMethodsBuilder x = RestMethods
.create()
.beanFactory(rootBeanFactory)
@@ -4810,6 +5135,14 @@ public class RestContext extends BeanContext {
}
}
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(RestMethodsBuilder.class, x)
+ .beanCreateMethodFinder(RestMethodsBuilder.class,
resource)
+ .find("createRestMethods")
+ .withDefault(x)
+ .run();
+
return x;
}
@@ -4820,7 +5153,33 @@ public class RestContext extends BeanContext {
* @return The builder for the {@link RestChildren} object.
* @throws Exception An error occurred.
*/
- protected RestChildrenBuilder createRestChildren(Object resource)
throws Exception {
+ protected RestChildren createRestChildren(Object resource) throws
Exception {
+
+ RestChildren x = createRestChildrenBuilder(resource).build();
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(RestChildren.class, x)
+ .beanCreateMethodFinder(RestChildren.class, resource)
+ .find("createRestChildren")
+ .withDefault(x)
+ .run();
+
+ return x;
+ }
+
+ /**
+ * Instantiates the REST children builder for this REST resource.
+ *
+ * <p>
+ * Allows subclasses to intercept and modify the builder used by the
{@link #createRestChildren(Object)} method.
+ *
+ * @param resource The REST resource object.
+ * @return The REST children builder for this REST resource.
+ * @throws Exception If REST children builder could not be instantiated.
+ */
+ protected RestChildrenBuilder createRestChildrenBuilder(Object
resource) throws Exception {
+
RestChildrenBuilder x = RestChildren
.create()
.beanFactory(rootBeanFactory)
@@ -4865,6 +5224,15 @@ public class RestContext extends BeanContext {
x.add(cc);
}
+
+ x = BeanFactory
+ .of(beanFactory, resource)
+ .addBean(RestChildrenBuilder.class, x)
+ .beanCreateMethodFinder(RestChildrenBuilder.class,
resource)
+ .find("createRestChildren")
+ .withDefault(x)
+ .run();
+
return x;
}
@@ -5177,6 +5545,21 @@ public class RestContext extends BeanContext {
}
/**
+ * Returns the Swagger provider used by this resource.
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_swaggerProviderClass}
+ * </ul>
+ *
+ * @return
+ * The information provider for this resource.
+ * <br>Never <jk>null</jk>.
+ */
+ public SwaggerProvider getSwaggerProvider() {
+ return swaggerProvider;
+ }
+
+ /**
* Returns the resource object.
*
* <p>
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerProvider.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerProvider.java
new file mode 100644
index 0000000..ea01244
--- /dev/null
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerProvider.java
@@ -0,0 +1,1320 @@
+//
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance *
+// * with the License. You may obtain a copy of the License at
*
+// *
*
+// * http://www.apache.org/licenses/LICENSE-2.0
*
+// *
*
+// * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the *
+// * specific language governing permissions and limitations under the
License. *
+//
***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static org.apache.juneau.internal.ObjectUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.StringUtils.isEmpty;
+import static org.apache.juneau.rest.RestParamType.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.collections.*;
+import org.apache.juneau.cp.*;
+import org.apache.juneau.dto.swagger.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.http.annotation.Contact;
+import org.apache.juneau.http.annotation.License;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.jsonschema.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.jsonschema.annotation.Items;
+import org.apache.juneau.jsonschema.annotation.Tag;
+import org.apache.juneau.marshall.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.reflect.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.util.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.svl.*;
+
+/**
+ * Interface for retrieving Swagger on a REST resource.
+ */
+public class SwaggerProvider {
+
+ /**
+ * Creator.
+ *
+ * @return A new builder for this object.
+ */
+ public static SwaggerProviderBuilder create() {
+ return new SwaggerProviderBuilder();
+ }
+
+ private final VarResolverSession vr;
+ private final JsonParser jp =
JsonParser.create().ignoreUnknownBeanProperties().build();
+ private final JsonSchemaGeneratorSession js;
+ private final Messages messages;
+ private final FileFinder fileFinder;
+
+ /**
+ * Constructor.
+ *
+ * @param builder The builder containing the settings for this Swagger
provider.
+ */
+ public SwaggerProvider(SwaggerProviderBuilder builder) {
+ BeanFactory bf = builder.beanFactory;
+
+ this.vr = firstNonNull(builder.varResolver,
bf.getBean(VarResolver.class).orElse(VarResolver.DEFAULT)).createSession();
+ this.js = firstNonNull(builder.jsonSchemaGenerator,
bf.getBean(JsonSchemaGenerator.class).orElse(JsonSchemaGenerator.DEFAULT)).createSession();
+ this.messages = builder.messages;
+ this.fileFinder = builder.fileFinder;
+ }
+
+ /**
+ * Returns the Swagger associated with the specified {@link
Rest}-annotated class.
+ *
+ * @param context The context of the {@link Rest}-annotated class.
+ * @param locale The request locale.
+ * @return A new {@link Swagger} object.
+ * @throws Exception If an error occurred producing the Swagger.
+ */
+ public Swagger getSwagger(RestContext context, Locale locale) throws
Exception {
+
+ Class<?> c = context.getResourceClass();
+ ClassInfo rci = ClassInfo.of(c);
+
+ FileFinder ff = fileFinder != null ? fileFinder :
FileFinder.create().cp(c,null,false).build();
+ Messages mb = messages != null ? messages :
Messages.create(c).build();
+
+ InputStream is = ff.getStream(rci.getSimpleName() + ".json",
locale).orElse(null);
+
+ // Load swagger JSON from classpath.
+ OMap omSwagger = SimpleJson.DEFAULT.read(is, OMap.class);
+ if (omSwagger == null)
+ omSwagger = new OMap();
+
+ // Combine it with @Rest(swagger)
+ for (Rest rr : rci.getAnnotations(Rest.class)) {
+
+ OMap sInfo = omSwagger.getMap("info", true);
+
+ sInfo
+ .appendSkipEmpty("title",
+ firstNonEmpty(
+ sInfo.getString("title"),
+ resolve(rr.title())
+ )
+ )
+ .appendSkipEmpty("description",
+ firstNonEmpty(
+ sInfo.getString("description"),
+ resolve(rr.description())
+ )
+ );
+
+ ResourceSwagger r = rr.swagger();
+
+ omSwagger.append(parseMap(r.value(),
"@ResourceSwagger(value) on class {0}", c));
+
+ if (! ResourceSwaggerAnnotation.empty(r)) {
+ OMap info = omSwagger.getMap("info", true);
+
+ info
+ .appendSkipEmpty("title",
resolve(r.title()))
+ .appendSkipEmpty("description",
resolve(r.description()))
+ .appendSkipEmpty("version",
resolve(r.version()))
+ .appendSkipEmpty("termsOfService",
resolve(r.termsOfService()))
+ .appendSkipEmpty("contact",
+ merge(
+ info.getMap("contact"),
+ toMap(r.contact(),
"@ResourceSwagger(contact) on class {0}", c)
+ )
+ )
+ .appendSkipEmpty("license",
+ merge(
+ info.getMap("license"),
+ toMap(r.license(),
"@ResourceSwagger(license) on class {0}", c)
+ )
+ );
+ }
+
+ omSwagger
+ .appendSkipEmpty("externalDocs",
+ merge(
+
omSwagger.getMap("externalDocs"),
+ toMap(r.externalDocs(),
"@ResourceSwagger(externalDocs) on class {0}", c)
+ )
+ )
+ .appendSkipEmpty("tags",
+ merge(
+ omSwagger.getList("tags"),
+ toList(r.tags(),
"@ResourceSwagger(tags) on class {0}", c)
+ )
+ );
+ }
+
+ omSwagger.appendSkipEmpty("externalDocs",
parseMap(mb.findFirstString("externalDocs"), "Messages/externalDocs on class
{0}", c));
+
+ OMap info = omSwagger.getMap("info", true);
+
+ info
+ .appendSkipEmpty("title",
resolve(mb.findFirstString("title")))
+ .appendSkipEmpty("description",
resolve(mb.findFirstString("description")))
+ .appendSkipEmpty("version",
resolve(mb.findFirstString("version")))
+ .appendSkipEmpty("termsOfService",
resolve(mb.findFirstString("termsOfService")))
+ .appendSkipEmpty("contact",
parseMap(mb.findFirstString("contact"), "Messages/contact on class {0}", c))
+ .appendSkipEmpty("license",
parseMap(mb.findFirstString("license"), "Messages/license on class {0}", c));
+
+ if (info.isEmpty())
+ omSwagger.remove("info");
+
+ OList
+ produces = omSwagger.getList("produces", true),
+ consumes = omSwagger.getList("consumes", true);
+ if (consumes.isEmpty())
+ consumes.addAll(context.getConsumes());
+ if (produces.isEmpty())
+ produces.addAll(context.getProduces());
+
+ Map<String,OMap> tagMap = new LinkedHashMap<>();
+ if (omSwagger.containsKey("tags")) {
+ for (OMap om :
omSwagger.getList("tags").elements(OMap.class)) {
+ String name = om.getString("name");
+ if (name == null)
+ throw new SwaggerException(null, "Tag
definition found without name in swagger JSON.");
+ tagMap.put(name, om);
+ }
+ }
+
+ String s = mb.findFirstString("tags");
+ if (s != null) {
+ for (OMap m : parseListOrCdl(s, "Messages/tags on class
{0}", c).elements(OMap.class)) {
+ String name = m.getString("name");
+ if (name == null)
+ throw new SwaggerException(null, "Tag
definition found without name in resource bundle on class {0}", c) ;
+ if (tagMap.containsKey(name))
+ tagMap.get(name).putAll(m);
+ else
+ tagMap.put(name, m);
+ }
+ }
+
+ // Load our existing bean definitions into our session.
+ OMap definitions = omSwagger.getMap("definitions", true);
+ for (String defId : definitions.keySet())
+ js.addBeanDef(defId, new
OMap(definitions.getMap(defId)));
+
+ // Iterate through all the @RestMethod methods.
+ for (RestMethodContext sm : context.getMethodContexts()) {
+
+ BeanSession bs = sm.createBeanSession();
+
+ Method m = sm.method;
+ MethodInfo mi = MethodInfo.of(m);
+ RestMethod rm = mi.getLastAnnotation(RestMethod.class);
+ String mn = m.getName();
+
+ // Get the operation from the existing swagger so far.
+ OMap op = getOperation(omSwagger, sm.getPathPattern(),
sm.getHttpMethod().toLowerCase());
+
+ // Add @RestMethod(swagger)
+ MethodSwagger ms = rm.swagger();
+
+ op.append(parseMap(ms.value(), "@MethodSwagger(value)
on class {0} method {1}", c, m));
+ op.appendSkipEmpty("operationId",
+ firstNonEmpty(
+ resolve(ms.operationId()),
+ op.getString("operationId"),
+ mn
+ )
+ );
+ op.appendSkipEmpty("summary",
+ firstNonEmpty(
+ resolve(ms.summary()),
+ resolve(mb.findFirstString(mn +
".summary")),
+ op.getString("summary"),
+ resolve(rm.summary())
+ )
+ );
+ op.appendSkipEmpty("description",
+ firstNonEmpty(
+ resolve(ms.description()),
+ resolve(mb.findFirstString(mn +
".description")),
+ op.getString("description"),
+ resolve(rm.description())
+ )
+ );
+ op.appendSkipEmpty("deprecated",
+ firstNonEmpty(
+ resolve(ms.deprecated()),
+ (m.getAnnotation(Deprecated.class) !=
null || m.getDeclaringClass().getAnnotation(Deprecated.class) != null) ? "true"
: null
+ )
+ );
+ op.appendSkipEmpty("tags",
+ merge(
+ parseListOrCdl(mb.findFirstString(mn +
".tags"), "Messages/tags on class {0} method {1}", c, m),
+ parseListOrCdl(ms.tags(),
"@MethodSwagger(tags) on class {0} method {1}", c, m)
+ )
+ );
+ op.appendSkipEmpty("schemes",
+ merge(
+ parseListOrCdl(mb.findFirstString(mn +
".schemes"), "Messages/schemes on class {0} method {1}", c, m),
+ parseListOrCdl(ms.schemes(),
"@MethodSwagger(schemes) on class {0} method {1}", c, m)
+ )
+ );
+ op.appendSkipEmpty("consumes",
+ firstNonEmpty(
+ parseListOrCdl(mb.findFirstString(mn +
".consumes"), "Messages/consumes on class {0} method {1}", c, m),
+ parseListOrCdl(ms.consumes(),
"@MethodSwagger(consumes) on class {0} method {1}", c, m)
+ )
+ );
+ op.appendSkipEmpty("produces",
+ firstNonEmpty(
+ parseListOrCdl(mb.findFirstString(mn +
".produces"), "Messages/produces on class {0} method {1}", c, m),
+ parseListOrCdl(ms.produces(),
"@MethodSwagger(produces) on class {0} method {1}", c, m)
+ )
+ );
+ op.appendSkipEmpty("parameters",
+ merge(
+ parseList(mb.findFirstString(mn +
".parameters"), "Messages/parameters on class {0} method {1}", c, m),
+ parseList(ms.parameters(),
"@MethodSwagger(parameters) on class {0} method {1}", c, m)
+ )
+ );
+ op.appendSkipEmpty("responses",
+ merge(
+ parseMap(mb.findFirstString(mn +
".responses"), "Messages/responses on class {0} method {1}", c, m),
+ parseMap(ms.responses(),
"@MethodSwagger(responses) on class {0} method {1}", c, m)
+ )
+ );
+ op.appendSkipEmpty("externalDocs",
+ merge(
+ op.getMap("externalDocs"),
+ parseMap(mb.findFirstString(mn +
".externalDocs"), "Messages/externalDocs on class {0} method {1}", c, m),
+ toMap(ms.externalDocs(),
"@MethodSwagger(externalDocs) on class {0} method {1}", c, m)
+ )
+ );
+
+ if (op.containsKey("tags"))
+ for (String tag :
op.getList("tags").elements(String.class))
+ if (! tagMap.containsKey(tag))
+ tagMap.put(tag, OMap.of("name",
tag));
+
+ OMap paramMap = new OMap();
+ if (op.containsKey("parameters"))
+ for (OMap param :
op.getList("parameters").elements(OMap.class))
+ paramMap.put(param.getString("in") +
'.' + ("body".equals(param.getString("in")) ? "body" :
param.getString("name")), param);
+
+ // Finally, look for parameters defined on method.
+ for (ParamInfo mpi : mi.getParams()) {
+
+ ClassInfo pt = mpi.getParameterType();
+ Type type = pt.innerType();
+
+ if (mpi.hasAnnotation(Body.class) ||
pt.hasAnnotation(Body.class)) {
+ OMap param = paramMap.getMap(BODY +
".body", true).a("in", BODY);
+ for (Body a :
mpi.getAnnotations(Body.class))
+ merge(param, a);
+ for (Body a :
pt.getAnnotations(Body.class))
+ merge(param, a);
+ param.putIfAbsent("required", true);
+ param.appendSkipEmpty("schema",
getSchema(param.getMap("schema"), type, bs));
+ addBodyExamples(sm, param, false, type,
locale);
+
+ } else if (mpi.hasAnnotation(Query.class) ||
pt.hasAnnotation(Query.class)) {
+ String name = null;
+ for (Query a :
mpi.getAnnotations(Query.class))
+ name = firstNonEmpty(a.name(),
a.n(), a.value(), name);
+ for (Query a :
pt.getAnnotations(Query.class))
+ name = firstNonEmpty(a.name(),
a.n(), a.value(), name);
+ OMap param = paramMap.getMap(QUERY +
"." + name, true).a("name", name).a("in", QUERY);
+ for (Query a :
mpi.getAnnotations(Query.class))
+ merge(param, a);
+ for (Query a :
pt.getAnnotations(Query.class))
+ merge(param, a);
+ mergePartSchema(param,
getSchema(param.getMap("schema"), type, bs));
+ addParamExample(sm, param, QUERY, type);
+
+ } else if (mpi.hasAnnotation(FormData.class) ||
pt.hasAnnotation(FormData.class)) {
+ String name = null;
+ for (FormData a :
mpi.getAnnotations(FormData.class))
+ name = firstNonEmpty(a.name(),
a.n(), a.value(), name);
+ for (FormData a :
pt.getAnnotations(FormData.class))
+ name = firstNonEmpty(a.name(),
a.n(), a.value(), name);
+ OMap param = paramMap.getMap(FORM_DATA
+ "." + name, true).a("name", name).a("in", FORM_DATA);
+ for (FormData a :
mpi.getAnnotations(FormData.class))
+ merge(param, a);
+ for (FormData a :
pt.getAnnotations(FormData.class))
+ merge(param, a);
+ mergePartSchema(param,
getSchema(param.getMap("schema"), type, bs));
+ addParamExample(sm, param, FORM_DATA,
type);
+
+ } else if (mpi.hasAnnotation(Header.class) ||
pt.hasAnnotation(Header.class)) {
+ String name = null;
+ for (Header a :
mpi.getAnnotations(Header.class))
+ name = firstNonEmpty(a.name(),
a.n(), a.value(), name);
+ for (Header a :
pt.getAnnotations(Header.class))
+ name = firstNonEmpty(a.name(),
a.n(), a.value(), name);
+ OMap param = paramMap.getMap(HEADER +
"." + name, true).a("name", name).a("in", HEADER);
+ for (Header a :
mpi.getAnnotations(Header.class))
+ merge(param, a);
+ for (Header a :
pt.getAnnotations(Header.class))
+ merge(param, a);
+ mergePartSchema(param,
getSchema(param.getMap("schema"), type, bs));
+ addParamExample(sm, param, HEADER,
type);
+
+ } else if (mpi.hasAnnotation(Path.class) ||
pt.hasAnnotation(Path.class)) {
+ String name = null;
+ for (Path a :
mpi.getAnnotations(Path.class))
+ name = firstNonEmpty(a.name(),
a.n(), a.value(), name);
+ for (Path a :
pt.getAnnotations(Path.class))
+ name = firstNonEmpty(a.name(),
a.n(), a.value(), name);
+ OMap param = paramMap.getMap(PATH + "."
+ name, true).a("name", name).a("in", PATH);
+ for (Path a :
mpi.getAnnotations(Path.class))
+ merge(param, a);
+ for (Path a :
pt.getAnnotations(Path.class))
+ merge(param, a);
+ mergePartSchema(param,
getSchema(param.getMap("schema"), type, bs));
+ addParamExample(sm, param, PATH, type);
+ param.putIfAbsent("required", true);
+ }
+ }
+
+ if (! paramMap.isEmpty())
+ op.put("parameters", paramMap.values());
+
+ OMap responses = op.getMap("responses", true);
+
+ for (ClassInfo eci : mi.getExceptionTypes()) {
+ if (eci.hasAnnotation(Response.class)) {
+ List<Response> la =
eci.getAnnotations(Response.class);
+ Set<Integer> codes = getCodes(la, 500);
+ for (Response a : la) {
+ for (Integer code : codes) {
+ OMap om =
responses.getMap(String.valueOf(code), true);
+ merge(om, a);
+ if (!
om.containsKey("schema"))
+
om.appendSkipEmpty("schema", getSchema(om.getMap("schema"), eci.inner(), bs));
+ }
+ }
+ for (MethodInfo ecmi :
eci.getAllMethodsParentFirst()) {
+ ResponseHeader a =
ecmi.getLastAnnotation(ResponseHeader.class);
+ if (a == null)
+ a =
ecmi.getReturnType().unwrap(Value.class,Optional.class).getLastAnnotation(ResponseHeader.class);
+ if (a != null && ! isMulti(a)) {
+ String ha = a.name();
+ for (Integer code :
codes) {
+ OMap header =
responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(ha,
true);
+ merge(header,
a);
+
mergePartSchema(header, getSchema(header, ecmi.getReturnType().innerType(),
bs));
+ }
+ }
+ }
+ }
+ }
+
+ if (mi.hasAnnotation(Response.class) ||
mi.getReturnType().unwrap(Value.class,Optional.class).hasAnnotation(Response.class))
{
+ List<Response> la =
mi.getAnnotations(Response.class);
+ Set<Integer> codes = getCodes(la, 200);
+ for (Response a : la) {
+ for (Integer code : codes) {
+ OMap om =
responses.getMap(String.valueOf(code), true);
+ merge(om, a);
+ if (! om.containsKey("schema"))
+
om.appendSkipEmpty("schema", getSchema(om.getMap("schema"),
m.getGenericReturnType(), bs));
+ addBodyExamples(sm, om, true,
m.getGenericReturnType(), locale);
+ }
+ }
+ if
(mi.getReturnType().hasAnnotation(Response.class)) {
+ for (MethodInfo ecmi :
mi.getReturnType().getAllMethodsParentFirst()) {
+ if
(ecmi.hasAnnotation(ResponseHeader.class)) {
+ ResponseHeader a =
ecmi.getLastAnnotation(ResponseHeader.class);
+ String ha = a.name();
+ if (! isMulti(a)) {
+ for (Integer
code : codes) {
+ OMap
header = responses.getMap(String.valueOf(code), true).getMap("headers",
true).getMap(ha, true);
+
merge(header, a);
+
mergePartSchema(header, getSchema(header, ecmi.getReturnType().innerType(),
bs));
+ }
+ }
+ }
+ }
+ }
+ } else if (m.getGenericReturnType() != void.class) {
+ OMap om = responses.getMap("200", true);
+ if (! om.containsKey("schema"))
+ om.appendSkipEmpty("schema",
getSchema(om.getMap("schema"), m.getGenericReturnType(), bs));
+ addBodyExamples(sm, om, true,
m.getGenericReturnType(), locale);
+ }
+
+ // Finally, look for @ResponseHeader parameters defined
on method.
+ for (ParamInfo mpi : mi.getParams()) {
+
+ ClassInfo pt = mpi.getParameterType();
+
+ if (mpi.hasAnnotation(ResponseHeader.class) ||
pt.hasAnnotation(ResponseHeader.class)) {
+ List<ResponseHeader> la =
AList.of(mpi.getAnnotations(ResponseHeader.class)).a(pt.getAnnotations(ResponseHeader.class));
+ Set<Integer> codes = getCodes2(la, 200);
+ String name = null;
+ for (ResponseHeader a : la)
+ name = firstNonEmpty(a.name(),
a.n(), a.value(), name);
+ Type type =
mpi.getParameterType().innerType();
+ for (ResponseHeader a : la) {
+ if (! isMulti(a)) {
+ for (Integer code :
codes) {
+ OMap header =
responses.getMap(String.valueOf(code), true).getMap("headers",
true).getMap(name, true);
+ merge(header,
a);
+
mergePartSchema(header, getSchema(header, Value.getParameterType(type), bs));
+ }
+ }
+ }
+
+ } else if (mpi.hasAnnotation(Response.class) ||
pt.hasAnnotation(Response.class)) {
+ List<Response> la =
AList.of(mpi.getAnnotations(Response.class)).a(pt.getAnnotations(Response.class));
+ Set<Integer> codes = getCodes(la, 200);
+ Type type =
mpi.getParameterType().innerType();
+ for (Response a : la) {
+ for (Integer code : codes) {
+ OMap response =
responses.getMap(String.valueOf(code), true);
+ merge(response, a);
+ }
+ }
+ type = Value.getParameterType(type);
+ if (type != null) {
+ for (String code :
responses.keySet()) {
+ OMap om =
responses.getMap(code);
+ if (!
om.containsKey("schema"))
+
om.appendSkipEmpty("schema", getSchema(om.getMap("schema"), type, bs));
+ }
+ }
+ }
+ }
+
+ // Add default response descriptions.
+ for (Map.Entry<String,Object> e : responses.entrySet())
{
+ String key = e.getKey();
+ OMap val = responses.getMap(key);
+ if (StringUtils.isDecimal(key))
+ val.appendIf(false, true, true,
"description", RestUtils.getHttpResponseText(Integer.parseInt(key)));
+ }
+
+ if (responses.isEmpty())
+ op.remove("responses");
+ else
+ op.put("responses", new TreeMap<>(responses));
+
+ if (! op.containsKey("consumes")) {
+ List<MediaType> mConsumes =
sm.supportedContentTypes;
+ if (! mConsumes.equals(consumes))
+ op.put("consumes", mConsumes);
+ }
+
+ if (! op.containsKey("produces")) {
+ List<MediaType> mProduces =
sm.supportedAcceptTypes;
+ if (! mProduces.equals(produces))
+ op.put("produces", mProduces);
+ }
+ }
+
+ if (js.getBeanDefs() != null)
+ for (Map.Entry<String,OMap> e :
js.getBeanDefs().entrySet())
+ definitions.put(e.getKey(),
fixSwaggerExtensions(e.getValue()));
+ if (definitions.isEmpty())
+ omSwagger.remove("definitions");
+
+ if (! tagMap.isEmpty())
+ omSwagger.put("tags", tagMap.values());
+
+ if (consumes.isEmpty())
+ omSwagger.remove("consumes");
+ if (produces.isEmpty())
+ omSwagger.remove("produces");
+
+// try {
+// if (! omSwagger.isEmpty())
+// assertNoEmpties(omSwagger);
+// } catch (SwaggerException e1) {
+//
System.err.println(omSwagger.toString(SimpleJsonSerializer.DEFAULT_READABLE));
+// throw e1;
+// }
+
+ try {
+ String swaggerJson =
SimpleJsonSerializer.DEFAULT_READABLE.toString(omSwagger);
+// System.err.println(swaggerJson);
+ return jp.parse(swaggerJson, Swagger.class);
+ } catch (Exception e) {
+ throw new RestServletException(e, "Error detected in
swagger.");
+ }
+ }
+
//=================================================================================================================
+ // Utility methods
+
//=================================================================================================================
+
+ private boolean isMulti(ResponseHeader h) {
+ if ("*".equals(h.name()) || "*".equals(h.value()))
+ return true;
+ return false;
+ }
+
+ private OMap resolve(OMap om) throws ParseException {
+ OMap om2 = null;
+ if (om.containsKey("_value")) {
+ om = om.modifiable();
+ om2 = parseMap(om.remove("_value"));
+ } else {
+ om2 = new OMap();
+ }
+ for (Map.Entry<String,Object> e : om.entrySet()) {
+ Object val = e.getValue();
+ if (val instanceof OMap) {
+ val = resolve((OMap)val);
+ } else if (val instanceof OList) {
+ val = resolve((OList) val);
+ } else if (val instanceof String) {
+ val = resolve(val.toString());
+ }
+ om2.put(e.getKey(), val);
+ }
+ return om2;
+ }
+
+ private OList resolve(OList om) throws ParseException {
+ OList ol2 = new OList();
+ for (Object val : om) {
+ if (val instanceof OMap) {
+ val = resolve((OMap)val);
+ } else if (val instanceof OList) {
+ val = resolve((OList) val);
+ } else if (val instanceof String) {
+ val = resolve(val.toString());
+ }
+ ol2.add(val);
+ }
+ return ol2;
+ }
+
+ private String resolve(String[]...s) {
+ for (String[] ss : s) {
+ if (ss.length != 0)
+ return resolve(joinnl(ss));
+ }
+ return null;
+ }
+
+ private String resolve(String s) {
+ if (s == null)
+ return null;
+ return vr.resolve(s.trim());
+ }
+
+ private OMap parseMap(String[] o, String location, Object...args)
throws ParseException {
+ if (o.length == 0)
+ return OMap.EMPTY_MAP;
+ try {
+ return parseMap(o);
+ } catch (ParseException e) {
+ throw new SwaggerException(e, "Malformed swagger JSON
object encountered in " + location + ".", args);
+ }
+ }
+
+ private OMap parseMap(String o, String location, Object...args) throws
ParseException {
+ try {
+ return parseMap(o);
+ } catch (ParseException e) {
+ throw new SwaggerException(e, "Malformed swagger JSON
object encountered in " + location + ".", args);
+ }
+ }
+
+ private OMap parseMap(Object o) throws ParseException {
+ if (o == null)
+ return null;
+ if (o instanceof String[])
+ o = joinnl((String[])o);
+ if (o instanceof String) {
+ String s = o.toString();
+ if (s.isEmpty())
+ return null;
+ s = resolve(s);
+ if ("IGNORE".equalsIgnoreCase(s))
+ return OMap.of("ignore", true);
+ if (! isJsonObject(s, true))
+ s = "{" + s + "}";
+ return OMap.ofJson(s);
+ }
+ if (o instanceof OMap)
+ return (OMap)o;
+ throw new SwaggerException(null, "Unexpected data type ''{0}''.
Expected OMap or String.", o.getClass().getName());
+ }
+
+ private OList parseList(Object o, String location,
Object...locationArgs) throws ParseException {
+ try {
+ if (o == null)
+ return null;
+ String s = (o instanceof String[] ? joinnl((String[])o)
: o.toString());
+ if (s.isEmpty())
+ return null;
+ s = resolve(s);
+ if (! isJsonArray(s, true))
+ s = "[" + s + "]";
+ return OList.ofJson(s);
+ } catch (ParseException e) {
+ throw new SwaggerException(e, "Malformed swagger JSON
array encountered in "+location+".", locationArgs);
+ }
+ }
+
+ private OList parseListOrCdl(Object o, String location,
Object...locationArgs) throws ParseException {
+ try {
+ if (o == null)
+ return null;
+ String s = (o instanceof String[] ? joinnl((String[])o)
: o.toString());
+ if (s.isEmpty())
+ return null;
+ s = resolve(s);
+ return StringUtils.parseListOrCdl(s);
+ } catch (ParseException e) {
+ throw new SwaggerException(e, "Malformed swagger JSON
array encountered in "+location+".", locationArgs);
+ }
+ }
+
+ private OMap newMap(OMap om, String[] value, String location,
Object...locationArgs) throws ParseException {
+ if (value.length == 0)
+ return om == null ? new OMap() : om;
+ OMap om2 = parseMap(joinnl(value), location,
locationArgs);
+ if (om == null)
+ return om2;
+ return om.append(om2);
+ }
+
+ private OMap merge(OMap...maps) {
+ OMap m = maps[0];
+ for (int i = 1; i < maps.length; i++) {
+ if (maps[i] != null) {
+ if (m == null)
+ m = new OMap();
+ m.putAll(maps[i]);
+ }
+ }
+ return m;
+ }
+
+ private OList merge(OList...lists) {
+ OList l = lists[0];
+ for (int i = 1; i < lists.length; i++) {
+ if (lists[i] != null) {
+ if (l == null)
+ l = new OList();
+ l.addAll(lists[i]);
+ }
+ }
+ return l;
+ }
+
+ @SafeVarargs
+ private final <T> T firstNonEmpty(T...t) {
+ return ObjectUtils.firstNonEmpty(t);
+ }
+
+ private OMap toMap(ExternalDocs a, String location,
Object...locationArgs) throws ParseException {
+ if (ExternalDocsAnnotation.empty(a))
+ return null;
+ OMap om = newMap(new OMap(), a.value(), location, locationArgs)
+ .appendSkipEmpty("description",
resolve(joinnl(a.description())))
+ .appendSkipEmpty("url", resolve(a.url()));
+ return nullIfEmpty(om);
+ }
+
+ private OMap toMap(Contact a, String location, Object...locationArgs)
throws ParseException {
+ if (ContactAnnotation.empty(a))
+ return null;
+ OMap om = newMap(new OMap(), a.value(), location, locationArgs)
+ .appendSkipEmpty("name", resolve(a.name()))
+ .appendSkipEmpty("url", resolve(a.url()))
+ .appendSkipEmpty("email", resolve(a.email()));
+ return nullIfEmpty(om);
+ }
+
+ private OMap toMap(License a, String location, Object...locationArgs)
throws ParseException {
+ if (LicenseAnnotation.empty(a))
+ return null;
+ OMap om = newMap(new OMap(), a.value(), location, locationArgs)
+ .appendSkipEmpty("name", resolve(a.name()))
+ .appendSkipEmpty("url", resolve(a.url()));
+ return nullIfEmpty(om);
+ }
+
+ private OMap toMap(Tag a, String location, Object...locationArgs)
throws ParseException {
+ OMap om = newMap(new OMap(), a.value(), location, locationArgs);
+ om
+ .appendSkipEmpty("name", resolve(a.name()))
+ .appendSkipEmpty("description",
resolve(joinnl(a.description())))
+ .appendSkipNull("externalDocs",
merge(om.getMap("externalDocs"), toMap(a.externalDocs(), location,
locationArgs)));
+ return nullIfEmpty(om);
+ }
+
+ private OList toList(Tag[] aa, String location, Object...locationArgs)
throws ParseException {
+ if (aa.length == 0)
+ return null;
+ OList ol = new OList();
+ for (Tag a : aa)
+ ol.add(toMap(a, location, locationArgs));
+ return nullIfEmpty(ol);
+ }
+
+ private OMap getSchema(OMap schema, Type type, BeanSession bs) throws
Exception {
+
+ if (type == Swagger.class)
+ return null;
+
+ schema = newMap(schema);
+
+ ClassMeta<?> cm = bs.getClassMeta(type);
+
+ if (schema.getBoolean("ignore", false))
+ return null;
+
+ if (schema.containsKey("type") || schema.containsKey("$ref"))
+ return schema;
+
+ OMap om = fixSwaggerExtensions(schema.append(js.getSchema(cm)));
+
+ return nullIfEmpty(om);
+ }
+
+ /**
+ * Replaces non-standard JSON-Schema attributes with standard Swagger
attributes.
+ */
+ private OMap fixSwaggerExtensions(OMap om) {
+ om
+ .appendSkipNull("discriminator",
om.remove("x-discriminator"))
+ .appendSkipNull("readOnly", om.remove("x-readOnly"))
+ .appendSkipNull("xml", om.remove("x-xml"))
+ .appendSkipNull("externalDocs",
om.remove("x-externalDocs"))
+ .appendSkipNull("example", om.remove("x-example"));
+ return nullIfEmpty(om);
+ }
+
+ private void addBodyExamples(RestMethodContext sm, OMap piri, boolean
response, Type type, Locale locale) throws Exception {
+
+ String sex = piri.getString("x-example");
+
+ if (sex == null) {
+ OMap schema = resolveRef(piri.getMap("schema"));
+ if (schema != null)
+ sex = schema.getString("example",
schema.getString("x-example"));
+ }
+
+ if (isEmpty(sex))
+ return;
+
+ Object example = null;
+ if (isJson(sex)) {
+ example = jp.parse(sex, type);
+ } else {
+ ClassMeta<?> cm = js.getClassMeta(type);
+ if (cm.hasStringMutater()) {
+ example = cm.getStringMutater().mutate(sex);
+ }
+ }
+
+ String examplesKey = response ? "examples" : "x-examples"; //
Parameters don't have an examples attribute.
+
+ OMap examples = piri.getMap(examplesKey);
+ if (examples == null)
+ examples = new OMap();
+
+ List<MediaType> mediaTypes = response ?
sm.getSerializers().getSupportedMediaTypes() :
sm.getParsers().getSupportedMediaTypes();
+
+ for (MediaType mt : mediaTypes) {
+ if (mt != MediaType.HTML) {
+ Serializer s2 =
sm.getSerializers().getSerializer(mt);
+ if (s2 != null) {
+ SerializerSessionArgs args =
+ SerializerSessionArgs
+ .create()
+ .locale(locale)
+ .mediaType(mt)
+ .useWhitespace(true)
+ ;
+ try {
+ String eVal =
s2.createSession(args).serializeToString(example);
+
examples.put(s2.getPrimaryMediaType().toString(), eVal);
+ } catch (Exception e) {
+ System.err.println("Could not
serialize to media type ["+mt+"]: " + e.getLocalizedMessage()); // NOT DEBUG
+ }
+ }
+ }
+ }
+
+ if (! examples.isEmpty())
+ piri.put(examplesKey, examples);
+ }
+
+ private void addParamExample(RestMethodContext sm, OMap piri,
RestParamType in, Type type) throws Exception {
+
+ String s = piri.getString("x-example");
+
+ if (isEmpty(s))
+ return;
+
+ OMap examples = piri.getMap("x-examples");
+ if (examples == null)
+ examples = new OMap();
+
+ String paramName = piri.getString("name");
+
+ if (in == QUERY)
+ s = "?" + urlEncodeLax(paramName) + "=" +
urlEncodeLax(s);
+ else if (in == FORM_DATA)
+ s = paramName + "=" + s;
+ else if (in == HEADER)
+ s = paramName + ": " + s;
+ else if (in == PATH)
+ s = sm.getPathPattern().replace("{"+paramName+"}",
urlEncodeLax(s));
+
+ examples.put("example", s);
+
+ if (! examples.isEmpty())
+ piri.put("x-examples", examples);
+ }
+
+
+ private OMap resolveRef(OMap m) {
+ if (m == null)
+ return null;
+ if (m.containsKey("$ref") && js.getBeanDefs() != null) {
+ String ref = m.getString("$ref");
+ if (ref.startsWith("#/definitions/"))
+ return js.getBeanDefs().get(ref.substring(14));
+ }
+ return m;
+ }
+
+ private OMap getOperation(OMap om, String path, String httpMethod) {
+ if (! om.containsKey("paths"))
+ om.put("paths", new OMap());
+ om = om.getMap("paths");
+ if (! om.containsKey(path))
+ om.put(path, new OMap());
+ om = om.getMap(path);
+ if (! om.containsKey(httpMethod))
+ om.put(httpMethod, new OMap());
+ return om.getMap(httpMethod);
+ }
+
+ private static OMap newMap(OMap om) {
+ if (om == null)
+ return new OMap();
+ return om.modifiable();
+ }
+
+ private OMap merge(OMap om, Body a) throws ParseException {
+ if (BodyAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.value().length > 0)
+ om.putAll(parseMap(a.value()));
+ if (a.api().length > 0)
+ om.putAll(parseMap(a.api()));
+ return om
+ .appendSkipEmpty("description",
resolve(a.description(), a.d()))
+ .appendSkipEmpty("x-example", resolve(a.example(),
a.ex()))
+ .appendSkipEmpty("x-examples", parseMap(a.examples()),
parseMap(a.exs()))
+ .appendSkipFalse("required", a.required() || a.r())
+ .appendSkipEmpty("schema", merge(om.getMap("schema"),
a.schema()))
+ ;
+ }
+
+ private OMap merge(OMap om, Query a) throws ParseException {
+ if (QueryAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.api().length > 0)
+ om.putAll(parseMap(a.api()));
+ return om
+ .appendSkipFalse("allowEmptyValue", a.allowEmptyValue()
|| a.aev())
+ .appendSkipEmpty("collectionFormat",
a.collectionFormat(), a.cf())
+ .appendSkipEmpty("default", joinnl(a._default(),
a.df()))
+ .appendSkipEmpty("description",
resolve(a.description(), a.d()))
+ .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e()))
+ .appendSkipEmpty("x-example", resolve(a.example(),
a.ex()))
+ .appendSkipFalse("exclusiveMaximum",
a.exclusiveMaximum() || a.emax())
+ .appendSkipFalse("exclusiveMinimum",
a.exclusiveMinimum() || a.emin())
+ .appendSkipEmpty("format", a.format(), a.f())
+ .appendSkipEmpty("items", merge(om.getMap("items"),
a.items()))
+ .appendSkipEmpty("maximum", a.maximum(), a.max())
+ .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi())
+ .appendSkipMinusOne("maxLength", a.maxLength(),
a.maxl())
+ .appendSkipEmpty("minimum", a.minimum(), a.min())
+ .appendSkipMinusOne("minItems", a.minItems(), a.mini())
+ .appendSkipMinusOne("minLength", a.minLength(),
a.minl())
+ .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo())
+ .appendSkipEmpty("pattern", a.pattern(), a.p())
+ .appendSkipFalse("required", a.required() || a.r())
+ .appendSkipEmpty("type", a.type(), a.t())
+ .appendSkipFalse("uniqueItems", a.uniqueItems() ||
a.ui())
+ ;
+ }
+
+ private OMap merge(OMap om, FormData a) throws ParseException {
+ if (FormDataAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.api().length > 0)
+ om.putAll(parseMap(a.api()));
+ return om
+ .appendSkipFalse("allowEmptyValue", a.allowEmptyValue()
|| a.aev())
+ .appendSkipEmpty("collectionFormat",
a.collectionFormat(), a.cf())
+ .appendSkipEmpty("default", joinnl(a._default(),
a.df()))
+ .appendSkipEmpty("description",
resolve(a.description(), a.d()))
+ .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e()))
+ .appendSkipEmpty("x-example", resolve(a.example(),
a.ex()))
+ .appendSkipFalse("exclusiveMaximum",
a.exclusiveMaximum() || a.emax())
+ .appendSkipFalse("exclusiveMinimum",
a.exclusiveMinimum() || a.emin())
+ .appendSkipEmpty("format", a.format(), a.f())
+ .appendSkipEmpty("items", merge(om.getMap("items"),
a.items()))
+ .appendSkipEmpty("maximum", a.maximum(), a.max())
+ .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi())
+ .appendSkipMinusOne("maxLength", a.maxLength(),
a.maxl())
+ .appendSkipEmpty("minimum", a.minimum(), a.min())
+ .appendSkipMinusOne("minItems", a.minItems(), a.mini())
+ .appendSkipMinusOne("minLength", a.minLength(),
a.minl())
+ .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo())
+ .appendSkipEmpty("pattern", a.pattern(), a.p())
+ .appendSkipFalse("required", a.required())
+ .appendSkipEmpty("type", a.type(), a.t())
+ .appendSkipFalse("uniqueItems", a.uniqueItems() ||
a.ui())
+ ;
+ }
+
+ private OMap merge(OMap om, Header a) throws ParseException {
+ if (HeaderAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.api().length > 0)
+ om.putAll(parseMap(a.api()));
+ return om
+ .appendSkipEmpty("collectionFormat",
a.collectionFormat(), a.cf())
+ .appendSkipEmpty("default", joinnl(a._default(),
a.df()))
+ .appendSkipEmpty("description",
resolve(a.description(), a.d()))
+ .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e()))
+ .appendSkipEmpty("x-example", resolve(a.example(),
a.ex()))
+ .appendSkipFalse("exclusiveMaximum",
a.exclusiveMaximum() || a.emax())
+ .appendSkipFalse("exclusiveMinimum",
a.exclusiveMinimum() || a.emin())
+ .appendSkipEmpty("format", a.format(), a.f())
+ .appendSkipEmpty("items", merge(om.getMap("items"),
a.items()))
+ .appendSkipEmpty("maximum", a.maximum(), a.max())
+ .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi())
+ .appendSkipMinusOne("maxLength", a.maxLength(),
a.maxl())
+ .appendSkipEmpty("minimum", a.minimum(), a.min())
+ .appendSkipMinusOne("minItems", a.minItems(), a.mini())
+ .appendSkipMinusOne("minLength", a.minLength(),
a.minl())
+ .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo())
+ .appendSkipEmpty("pattern", a.pattern(), a.p())
+ .appendSkipFalse("required", a.required() || a.r())
+ .appendSkipEmpty("type", a.type(), a.t())
+ .appendSkipFalse("uniqueItems", a.uniqueItems() ||
a.ui())
+ ;
+ }
+
+ private OMap merge(OMap om, Path a) throws ParseException {
+ if (PathAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.api().length > 0)
+ om.putAll(parseMap(a.api()));
+ return om
+ .appendSkipEmpty("collectionFormat",
a.collectionFormat(), a.cf())
+ .appendSkipEmpty("description",
resolve(a.description(), a.d()))
+ .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e()))
+ .appendSkipEmpty("x-example", resolve(a.example(),
a.ex()))
+ .appendSkipFalse("exclusiveMaximum",
a.exclusiveMaximum() || a.emax())
+ .appendSkipFalse("exclusiveMinimum",
a.exclusiveMinimum() || a.emin())
+ .appendSkipEmpty("format", a.format(), a.f())
+ .appendSkipEmpty("items", merge(om.getMap("items"),
a.items()))
+ .appendSkipEmpty("maximum", a.maximum(), a.max())
+ .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi())
+ .appendSkipMinusOne("maxLength", a.maxLength(),
a.maxl())
+ .appendSkipEmpty("minimum", a.minimum(), a.min())
+ .appendSkipMinusOne("minItems", a.minItems(), a.mini())
+ .appendSkipMinusOne("minLength", a.minLength(),
a.minl())
+ .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo())
+ .appendSkipEmpty("pattern", a.pattern(), a.p())
+ .appendSkipEmpty("type", a.type(), a.t())
+ .appendSkipFalse("uniqueItems", a.uniqueItems() ||
a.ui())
+ ;
+ }
+
+ private OMap merge(OMap om, Schema a) throws ParseException {
+ if (SchemaAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.value().length > 0)
+ om.putAll(parseMap(a.value()));
+ return om
+ .appendSkipEmpty("additionalProperties",
toOMap(a.additionalProperties()))
+ .appendSkipEmpty("allOf", joinnl(a.allOf()))
+ .appendSkipEmpty("collectionFormat",
a.collectionFormat(), a.cf())
+ .appendSkipEmpty("default", joinnl(a._default(),
a.df()))
+ .appendSkipEmpty("discriminator", a.discriminator())
+ .appendSkipEmpty("description",
resolve(a.description()), resolve(a.d()))
+ .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e()))
+ .appendSkipEmpty("x-example", resolve(a.example()),
resolve(a.ex()))
+ .appendSkipEmpty("examples", parseMap(a.examples()),
parseMap(a.exs()))
+ .appendSkipFalse("exclusiveMaximum",
a.exclusiveMaximum() || a.emax())
+ .appendSkipFalse("exclusiveMinimum",
a.exclusiveMinimum() || a.emin())
+ .appendSkipEmpty("externalDocs",
merge(om.getMap("externalDocs"), a.externalDocs()))
+ .appendSkipEmpty("format", a.format(), a.f())
+ .appendSkipEmpty("ignore", a.ignore() ? "true" : null)
+ .appendSkipEmpty("items", merge(om.getMap("items"),
a.items()))
+ .appendSkipEmpty("maximum", a.maximum(), a.max())
+ .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi())
+ .appendSkipMinusOne("maxLength", a.maxLength(),
a.maxl())
+ .appendSkipMinusOne("maxProperties", a.maxProperties(),
a.maxp())
+ .appendSkipEmpty("minimum", a.minimum(), a.min())
+ .appendSkipMinusOne("minItems", a.minItems(), a.mini())
+ .appendSkipMinusOne("minLength", a.minLength(),
a.minl())
+ .appendSkipMinusOne("minProperties", a.minProperties(),
a.minp())
+ .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo())
+ .appendSkipEmpty("pattern", a.pattern(), a.p())
+ .appendSkipEmpty("properties", toOMap(a.properties()))
+ .appendSkipFalse("readOnly", a.readOnly() || a.ro())
+ .appendSkipFalse("required", a.required() || a.r())
+ .appendSkipEmpty("title", a.title())
+ .appendSkipEmpty("type", a.type(), a.t())
+ .appendSkipFalse("uniqueItems", a.uniqueItems() ||
a.ui())
+ .appendSkipEmpty("xml", joinnl(a.xml()))
+ .appendSkipEmpty("$ref", a.$ref())
+ ;
+ }
+
+ private OMap merge(OMap om, ExternalDocs a) throws ParseException {
+ if (ExternalDocsAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.value().length > 0)
+ om.putAll(parseMap(a.value()));
+ return om
+ .appendSkipEmpty("description",
resolve(a.description()))
+ .appendSkipEmpty("url", a.url())
+ ;
+ }
+
+ private OMap merge(OMap om, Items a) throws ParseException {
+ if (ItemsAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.value().length > 0)
+ om.putAll(parseMap(a.value()));
+ return om
+ .appendSkipEmpty("collectionFormat",
a.collectionFormat(), a.cf())
+ .appendSkipEmpty("default", joinnl(a._default(),
a.df()))
+ .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e()))
+ .appendSkipEmpty("format", a.format(), a.f())
+ .appendSkipFalse("exclusiveMaximum",
a.exclusiveMaximum() || a.emax())
+ .appendSkipFalse("exclusiveMinimum",
a.exclusiveMinimum() || a.emin())
+ .appendSkipEmpty("items", merge(om.getMap("items"),
a.items()))
+ .appendSkipEmpty("maximum", a.maximum(), a.max())
+ .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi())
+ .appendSkipMinusOne("maxLength", a.maxLength(),
a.maxl())
+ .appendSkipEmpty("minimum", a.minimum(), a.min())
+ .appendSkipMinusOne("minItems", a.minItems(), a.mini())
+ .appendSkipMinusOne("minLength", a.minLength(),
a.minl())
+ .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo())
+ .appendSkipEmpty("pattern", a.pattern(), a.p())
+ .appendSkipFalse("uniqueItems", a.uniqueItems() ||
a.ui())
+ .appendSkipEmpty("type", a.type(), a.t())
+ .appendSkipEmpty("$ref", a.$ref())
+ ;
+ }
+
+ private OMap merge(OMap om, SubItems a) throws ParseException {
+ if (SubItemsAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.value().length > 0)
+ om.putAll(parseMap(a.value()));
+ return om
+ .appendSkipEmpty("collectionFormat",
a.collectionFormat(), a.cf())
+ .appendSkipEmpty("default", joinnl(a._default(),
a.df()))
+ .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e()))
+ .appendSkipFalse("exclusiveMaximum",
a.exclusiveMaximum() || a.emax())
+ .appendSkipFalse("exclusiveMinimum",
a.exclusiveMinimum() || a.emin())
+ .appendSkipEmpty("format", a.format(), a.f())
+ .appendSkipEmpty("items", toOMap(a.items()))
+ .appendSkipEmpty("maximum", a.maximum(), a.max())
+ .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi())
+ .appendSkipMinusOne("maxLength", a.maxLength(),
a.maxl())
+ .appendSkipEmpty("minimum", a.minimum(), a.min())
+ .appendSkipMinusOne("minItems", a.minItems(), a.mini())
+ .appendSkipMinusOne("minLength", a.minLength(),
a.minl())
+ .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo())
+ .appendSkipEmpty("pattern", a.pattern(), a.p())
+ .appendSkipEmpty("type", a.type(), a.t())
+ .appendSkipFalse("uniqueItems", a.uniqueItems() ||
a.ui())
+ .appendSkipEmpty("$ref", a.$ref())
+ ;
+ }
+
+ private OMap merge(OMap om, Response a) throws ParseException {
+ if (ResponseAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.api().length > 0)
+ om.putAll(parseMap(a.api()));
+ return om
+ .appendSkipEmpty("description",
resolve(a.description(), a.d()))
+ .appendSkipEmpty("x-example", resolve(a.example(),
a.ex()))
+ .appendSkipEmpty("examples", parseMap(a.examples()),
parseMap(a.exs()))
+ .appendSkipEmpty("headers", merge(om.getMap("headers"),
a.headers()))
+ .appendSkipEmpty("schema", merge(om.getMap("schema"),
a.schema()))
+ ;
+ }
+
+ private OMap merge(OMap om, ResponseHeader[] a) throws ParseException {
+ if (a.length == 0)
+ return om;
+ om = newMap(om);
+ for (ResponseHeader aa : a) {
+ String name = StringUtils.firstNonEmpty(aa.name(),
aa.value());
+ if (isEmpty(name))
+ throw new RuntimeException("@ResponseHeader
used without name or value.");
+ om.getMap(name, true).putAll(merge(null, aa));
+ }
+ return om;
+ }
+
+ private OMap merge(OMap om, ResponseHeader a) throws ParseException {
+ if (ResponseHeaderAnnotation.empty(a))
+ return om;
+ om = newMap(om);
+ if (a.api().length > 0)
+ om.putAll(parseMap(a.api()));
+ return om
+ .appendSkipEmpty("collectionFormat",
a.collectionFormat(), a.cf())
+ .appendSkipEmpty("default", joinnl(a._default(),
a.df()))
+ .appendSkipEmpty("description",
resolve(a.description(), a.d()))
+ .appendSkipEmpty("enum", toSet(a._enum()), toSet(a.e()))
+ .appendSkipEmpty("x-example", resolve(a.example(),
a.ex()))
+ .appendSkipFalse("exclusiveMaximum",
a.exclusiveMaximum() || a.emax())
+ .appendSkipFalse("exclusiveMinimum",
a.exclusiveMinimum() || a.emin())
+ .appendSkipEmpty("format", a.format(), a.f())
+ .appendSkipEmpty("items", merge(om.getMap("items"),
a.items()))
+ .appendSkipEmpty("maximum", a.maximum(), a.max())
+ .appendSkipMinusOne("maxItems", a.maxItems(), a.maxi())
+ .appendSkipMinusOne("maxLength", a.maxLength(),
a.maxl())
+ .appendSkipEmpty("minimum", a.minimum(), a.min())
+ .appendSkipMinusOne("minItems", a.minItems(), a.mini())
+ .appendSkipMinusOne("minLength", a.minLength(),
a.minl())
+ .appendSkipEmpty("multipleOf", a.multipleOf(), a.mo())
+ .appendSkipEmpty("pattern", a.pattern(), a.p())
+ .appendSkipEmpty("type", a.type(), a.t())
+ .appendSkipFalse("uniqueItems", a.uniqueItems() ||
a.ui())
+ .appendSkipEmpty("$ref", a.$ref())
+ ;
+ }
+
+ private OMap mergePartSchema(OMap param, OMap schema) {
+ if (schema != null) {
+ param
+ .appendIf(false, true, true,
"collectionFormat", schema.remove("collectionFormat"))
+ .appendIf(false, true, true, "default",
schema.remove("default"))
+ .appendIf(false, true, true, "description",
schema.remove("enum"))
+ .appendIf(false, true, true, "enum",
schema.remove("enum"))
+ .appendIf(false, true, true, "x-example",
schema.remove("x-example"))
+ .appendIf(false, true, true,
"exclusiveMaximum", schema.remove("exclusiveMaximum"))
+ .appendIf(false, true, true,
"exclusiveMinimum", schema.remove("exclusiveMinimum"))
+ .appendIf(false, true, true, "format",
schema.remove("format"))
+ .appendIf(false, true, true, "items",
schema.remove("items"))
+ .appendIf(false, true, true, "maximum",
schema.remove("maximum"))
+ .appendIf(false, true, true, "maxItems",
schema.remove("maxItems"))
+ .appendIf(false, true, true, "maxLength",
schema.remove("maxLength"))
+ .appendIf(false, true, true, "minimum",
schema.remove("minimum"))
+ .appendIf(false, true, true, "minItems",
schema.remove("minItems"))
+ .appendIf(false, true, true, "minLength",
schema.remove("minLength"))
+ .appendIf(false, true, true, "multipleOf",
schema.remove("multipleOf"))
+ .appendIf(false, true, true, "pattern",
schema.remove("pattern"))
+ .appendIf(false, true, true, "required",
schema.remove("required"))
+ .appendIf(false, true, true, "type",
schema.remove("type"))
+ .appendIf(false, true, true, "uniqueItems",
schema.remove("uniqueItems"));
+
+ if ("object".equals(param.getString("type")) && !
schema.isEmpty())
+ param.put("schema", schema);
+ }
+
+ return param;
+ }
+
+
+
+ private OMap toOMap(String[] ss) throws ParseException {
+ if (ss.length == 0)
+ return null;
+ String s = joinnl(ss);
+ if (s.isEmpty())
+ return null;
+ if (! isJsonObject(s, true))
+ s = "{" + s + "}";
+ s = resolve(s);
+ return OMap.ofJson(s);
+ }
+
+ private Set<String> toSet(String[] ss) throws ParseException {
+ if (ss.length == 0)
+ return null;
+ String s = joinnl(ss);
+ if (s.isEmpty())
+ return null;
+ s = resolve(s);
+ Set<String> set = ASet.of();
+ for (Object o : StringUtils.parseListOrCdl(s))
+ set.add(o.toString());
+ return set;
+ }
+
+ static String joinnl(String[]...s) {
+ for (String[] ss : s) {
+ if (ss.length != 0)
+ return StringUtils.joinnl(ss).trim();
+ }
+ return "";
+ }
+
+ private static Set<Integer> getCodes(List<Response> la, Integer def) {
+ Set<Integer> codes = new TreeSet<>();
+ for (Response a : la) {
+ for (int i : a.value())
+ codes.add(i);
+ for (int i : a.code())
+ codes.add(i);
+ }
+ if (codes.isEmpty() && def != null)
+ codes.add(def);
+ return codes;
+ }
+
+ private static Set<Integer> getCodes2(List<ResponseHeader> la, Integer
def) {
+ Set<Integer> codes = new TreeSet<>();
+ for (ResponseHeader a : la) {
+ for (int i : a.code())
+ codes.add(i);
+ }
+ if (codes.isEmpty() && def != null)
+ codes.add(def);
+ return codes;
+ }
+
+ private static OMap nullIfEmpty(OMap m) {
+ return (m == null || m.isEmpty() ? null : m);
+ }
+
+ private static OList nullIfEmpty(OList l) {
+ return (l == null || l.isEmpty() ? null : l);
+ }
+}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerProviderBuilder.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerProviderBuilder.java
new file mode 100644
index 0000000..f8d89f6
--- /dev/null
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/SwaggerProviderBuilder.java
@@ -0,0 +1,115 @@
+//
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance *
+// * with the License. You may obtain a copy of the License at
*
+// *
*
+// * http://www.apache.org/licenses/LICENSE-2.0
*
+// *
*
+// * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the *
+// * specific language governing permissions and limitations under the
License. *
+//
***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static org.apache.juneau.rest.HttpRuntimeException.*;
+
+import org.apache.juneau.cp.*;
+import org.apache.juneau.http.exception.*;
+import org.apache.juneau.jsonschema.*;
+import org.apache.juneau.svl.*;
+
+/**
+ * Builder class for {@link SwaggerProvider} objects.
+ */
+public class SwaggerProviderBuilder {
+
+ private Class<? extends SwaggerProvider> implClass;
+
+ BeanFactory beanFactory;
+ Class<?> resourceClass;
+ VarResolver varResolver;
+ JsonSchemaGenerator jsonSchemaGenerator;
+ Messages messages;
+ FileFinder fileFinder;
+
+ /**
+ * Creates a new {@link SwaggerProvider} object from this builder.
+ *
+ * @return A new {@link SwaggerProvider} object.
+ */
+ public SwaggerProvider build() {
+ try {
+ Class<? extends SwaggerProvider> ic = implClass == null
? SwaggerProvider.class : implClass;
+ return
BeanFactory.of(beanFactory).addBean(SwaggerProviderBuilder.class,
this).createBean(ic);
+ } catch (Exception e) {
+ throw toHttpException(e, InternalServerError.class);
+ }
+ }
+
+ /**
+ * Specifies the bean factory to use for instantiating the {@link
SwaggerProvider} object.
+ *
+ * @param value The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ public SwaggerProviderBuilder beanFactory(BeanFactory value) {
+ this.beanFactory = value;
+ return this;
+ }
+
+ /**
+ * Specifies the variable resolver to use for the {@link
SwaggerProvider} object.
+ *
+ * @param value The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ public SwaggerProviderBuilder varResolver(VarResolver value) {
+ this.varResolver = value;
+ return this;
+ }
+
+ /**
+ * Specifies the JSON-schema generator to use for the {@link
SwaggerProvider} object.
+ *
+ * @param value The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ public SwaggerProviderBuilder jsonSchemaGenerator(JsonSchemaGenerator
value) {
+ this.jsonSchemaGenerator = value;
+ return this;
+ }
+
+ /**
+ * Specifies the messages to use for the {@link SwaggerProvider} object.
+ *
+ * @param value The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ public SwaggerProviderBuilder messages(Messages value) {
+ this.messages = value;
+ return this;
+ }
+
+ /**
+ * Specifies the file-finder to use for the {@link SwaggerProvider}
object.
+ *
+ * @param value The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ public SwaggerProviderBuilder fileFinder(FileFinder value) {
+ this.fileFinder = value;
+ return this;
+ }
+
+ /**
+ * Specifies a subclass of {@link SwaggerProvider} to create when the
{@link #build()} method is called.
+ *
+ * @param value The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ public SwaggerProviderBuilder implClass(Class<? extends
SwaggerProvider> value) {
+ this.implClass = value;
+ return this;
+ }
+}