blob: 34a6ea2adfeb40b687979c73159d7031ec07fd4c [file] [log] [blame]
// ***************************************************************************************************************************
// * 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 javax.servlet.http.HttpServletResponse.*;
import static org.apache.juneau.dto.swagger.SwaggerBuilder.*;
import static org.apache.juneau.internal.ReflectionUtils.*;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
import org.apache.juneau.dto.swagger.*;
import org.apache.juneau.http.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.svl.*;
/**
* Default implementation of {@link RestInfoProvider}.
*
* <p>
* Subclasses can override these methods to tailor how HTTP REST resources are documented.
*
* <h5 class='section'>See Also:</h5>
* <ul>
* <li class='jf'>{@link RestContext#REST_infoProvider}
* <li class='link'><a class="doclink" href="../../../../overview-summary.html#juneau-rest-server.OptionsPages">Overview &gt; juneau-rest-server &gt; OPTIONS Pages</a>
* </ul>
*/
public class BasicRestInfoProvider implements RestInfoProvider {
private final RestContext context;
private final String
siteName,
title,
description,
termsOfService,
contact,
license,
version,
tags,
externalDocs;
private final ConcurrentHashMap<Locale,Swagger> swaggers = new ConcurrentHashMap<>();
/**
* Constructor.
*
* @param context The resource context.
*/
public BasicRestInfoProvider(RestContext context) {
this.context = context;
Builder b = new Builder(context);
this.siteName = b.siteName;
this.title = b.title;
this.description = b.description;
this.termsOfService = b.termsOfService;
this.contact = b.contact;
this.license = b.license;
this.version = b.version;
this.tags = b.tags;
this.externalDocs = b.externalDocs;
}
private static final class Builder {
String
siteName,
title,
description,
termsOfService,
contact,
license,
version,
tags,
externalDocs;
Builder(RestContext context) {
LinkedHashMap<Class<?>,RestResource> restResourceAnnotationsParentFirst = findAnnotationsMapParentFirst(RestResource.class, context.getResource().getClass());
for (RestResource r : restResourceAnnotationsParentFirst.values()) {
if (! r.siteName().isEmpty())
siteName = r.siteName();
if (! r.title().isEmpty())
title = r.title();
if (! r.description().isEmpty())
description = r.description();
ResourceSwagger sr = r.swagger();
if (! sr.termsOfService().isEmpty())
termsOfService = sr.termsOfService();
if (! sr.contact().isEmpty())
contact = sr.contact();
if (! sr.license().isEmpty())
license = sr.license();
if (! sr.version().isEmpty())
version = sr.version();
if (! sr.tags().isEmpty())
tags = sr.tags();
if (! sr.externalDocs().isEmpty())
externalDocs = sr.externalDocs();
}
}
}
/**
* Returns the localized Swagger from the file system.
*
* <p>
* Looks for a file called <js>"{ServletClass}_{locale}.json"</js> in the same package as this servlet and returns
* it as a parsed {@link Swagger} object.
*
* <p>
* Returned objects are cached per-locale for later quick-lookup.
*
* @param req The incoming HTTP request.
* @return The parsed swagger object, or <jk>null</jk> if none could be found.
* @throws Exception
* If swagger file was not valid JSON.
*/
public Swagger getSwaggerFromFile(RestRequest req) throws Exception {
Locale locale = req.getLocale();
Swagger s = swaggers.get(locale);
if (s == null) {
s = context.getClasspathResource(Swagger.class, MediaType.JSON, getClass().getSimpleName() + ".json", locale);
swaggers.putIfAbsent(locale, s == null ? Swagger.NULL : s);
}
return s == Swagger.NULL ? null : s;
}
/**
* Returns the localized swagger for this REST resource.
*
* <p>
* Subclasses can override this method to customize the Swagger.
*
* @param req The incoming HTTP request.
* @return
* A new Swagger instance.
* <br>Never <jk>null</jk>.
* @throws Exception
*/
@Override /* RestInfoProvider */
public Swagger getSwagger(RestRequest req) throws Exception {
// If a file is defined, use that.
Swagger s = getSwaggerFromFile(req);
if (s != null)
return s;
s = swagger(
info(getTitle(req), getVersion(req))
.contact(getContact(req))
.license(getLicense(req))
.description(getDescription(req))
.termsOfService(getTermsOfService(req))
)
.consumes(getConsumes(req))
.produces(getProduces(req))
.tags(getTags(req))
.externalDocs(getExternalDocs(req));
for (RestJavaMethod sm : context.getCallMethods().values()) {
if (sm.isRequestAllowed(req)) {
Method m = sm.method;
Operation o = operation()
.operationId(getMethodOperationId(m, req))
.description(getMethodDescription(m, req))
.tags(getMethodTags(m, req))
.summary(getMethodSummary(m, req))
.externalDocs(getMethodExternalDocs(m, req))
.parameters(getMethodParameters(m, req))
.responses(getMethodResponses(m, req));
if (isDeprecated(m, req))
o.deprecated(true);
o.consumes(getMethodConsumes(m, req));
o.produces(getMethodProduces(m, req));
s.path(
sm.getPathPattern(),
sm.getHttpMethod().toLowerCase(),
o
);
}
}
return s;
}
/**
* Returns the localized operation ID of the specified java method.
*
* <p>
* Subclasses can override this method to provide their own operation ID.
*
* <p>
* The default implementation simply returns the Java method name.
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return The localized operation ID of the method, or <jk>null</jk> if none was found.
* @throws Exception
*/
public String getMethodOperationId(Method method, RestRequest req) throws Exception {
return method.getName();
}
/**
* Returns the localized summary of the specified java method on this servlet.
*
* <p>
* Subclasses can override this method to provide their own summary.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link RestMethod#summary() @RestMethod.summary()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ja>@RestMethod</ja>(summary=<js>"Summary of my method"</js>)
* <jk>public</jk> Object myMethod() {...}
*
* <cc>// Pulled from some other location</cc>
* <ja>@RestMethod</ja>(summary=<js>"$L{myLocalizedSummary}"</js>)
* <jk>public</jk> Object myMethod() {...}
* </p>
* <li>Localized string from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].[javaMethodName].summary</ck>
* <li><ck>[javaMethodName].summary</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link RestMethod#summary() @RestMethod.summary()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.myMethod.summary</ck> = <cv>Summary of my method.</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.myMethod.summary</ck> = <cv>$C{MyStrings/MyClass.myMethod.summary}</cv>
* </p>
* </ol>
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return The localized summary of the method, or <jk>null</jk> if none was found.
* @throws Exception
*/
@Override /* RestInfoProvider */
public String getMethodSummary(Method method, RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
String s = method.getAnnotation(RestMethod.class).summary();
if (s.isEmpty())
s = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".summary");
if (s != null)
return vr.resolve(s);
Operation o = getSwaggerOperationFromFile(method, req);
return o == null ? null : o.getSummary();
}
/**
* Returns the localized description of the specified java method on this servlet.
*
* <p>
* Subclasses can override this method to provide their own description.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link RestMethod#description() @RestMethod.description()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ja>@RestMethod</ja>(description=<js>"Description of my method"</js>)
* <jk>public</jk> Object myMethod() {...}
*
* <cc>// Pulled from some other location</cc>
* <ja>@RestMethod</ja>(description=<js>"$L{myLocalizedDescription}"</js>)
* <jk>public</jk> Object myMethod() {...}
* </p>
* <li>Localized string from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].[javaMethodName].description</ck>
* <li><ck>[javaMethodName].description</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link RestMethod#description() @RestMethod.description()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.myMethod.description</ck> = <cv>Description of my method.</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.myMethod.description</ck> = <cv>$C{MyStrings/MyClass.myMethod.description}</cv>
* </p>
* </ol>
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return The localized description of the method, or <jk>null</jk> if none was found.
* @throws Exception
*/
@Override /* RestInfoProvider */
public String getMethodDescription(Method method, RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
String s = method.getAnnotation(RestMethod.class).description();
if (s.isEmpty())
s = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".description");
if (s != null)
return vr.resolve(s);
Operation o = getSwaggerOperationFromFile(method, req);
return o == null ? null : o.getDescription();
}
/**
* Returns the localized Swagger tags for this Java method.
*
* <p>
* Subclasses can override this method to provide their own tags.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link MethodSwagger#tags() @MethodSwagger.tags()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ja>@RestMethod</ja>(
* swagger=<ja>@MethodSwagger</ja>(tags=<js>"foo,bar,baz"</js>)
* )
* <jk>public</jk> Object myMethod() {...}
*
* <cc>// Pulled from some other location</cc>
* <ja>@RestMethod</ja>(
* swagger=<ja>@MethodSwagger</ja>(tags=<js>"$L{myLocalizedTags}"</js>)
* )
* <jk>public</jk> Object myMethod() {...}
* </p>
* <li>Localized string from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].[javaMethodName].tags</ck>
* <li><ck>[javaMethodName].tags</ck>
* </ol>
* <br>Value can be a comma-delimited list or JSON array.
* <br>Value can contain any SVL variables defined on the {@link MethodSwagger#tags() @MethodSwagger.tags()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Comma-delimited list</cc>
* <ck>MyClass.myMethod.tags</ck> = <cv>foo, bar, baz</cv>
*
* <cc>// JSON array</cc>
* <ck>MyClass.myMethod.tags</ck> = <cv>["foo", "bar", "baz"]</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.myMethod.description</ck> = <cv>$C{MyStrings/MyClass.myMethod.tags}</cv>
* </p>
* </ol>
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return The localized tags of the method, or <jk>null</jk> if none were found.
* @throws Exception
*/
public List<String> getMethodTags(Method method, RestRequest req) throws Exception {
JsonParser p = JsonParser.DEFAULT;
VarResolverSession vr = req.getVarResolverSession();
String s = method.getAnnotation(RestMethod.class).swagger().tags();
if (s.isEmpty())
s = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".tags");
if (s != null) {
s = vr.resolve(s);
if (StringUtils.isObjectList(s))
return p.parse(s, ArrayList.class, String.class);
return Arrays.asList(StringUtils.split(s));
}
Operation o = getSwaggerOperationFromFile(method, req);
return o == null ? null : o.getTags();
}
/**
* Returns the localized external documentation of the specified java method on this servlet.
*
* <p>
* Subclasses can override this method to provide their own external documentation.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link MethodSwagger#externalDocs() @MethodSwagger.externalDocs()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ja>@RestMethod</ja>(
* swagger=<ja>@MethodSwagger</ja>(externalDocs=<js>"{description:'Find more info here',url:'https://swagger.io'}"</js>)
* )
* <jk>public</jk> Object myMethod() {...}
*
* <cc>// Pulled from some other location</cc>
* <ja>@RestMethod</ja>(
* swagger=<ja>@MethodSwagger</ja>(externalDocs=<js>"$L{myLocalizedExternalDocs}"</js>)
* )
* <jk>public</jk> Object myMethod() {...}
* </p>
* <li>Localized string from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].[javaMethodName].externalDocs</ck>
* <li><ck>[javaMethodName].externalDocs</ck>
* </ol>
* <br>Value is a JSON representation of a {@link ExternalDocumentation} object.
* <br>Value can contain any SVL variables defined on the {@link MethodSwagger#externalDocs() @MethodSwagger.externalDocs()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.myMethod.externalDocs</ck> = <cv>{description:"Find more info here",url:"https://swagger.io"}</js>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.myMethod.externalDocs</ck> = <cv>$C{MyStrings/MyClass.myMethod.externalDocs}</cv>
* </p>
* </ol>
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return The localized external documentation of the method, or <jk>null</jk> if none was found.
* @throws Exception
*/
public ExternalDocumentation getMethodExternalDocs(Method method, RestRequest req) throws Exception {
JsonParser p = JsonParser.DEFAULT;
VarResolverSession vr = req.getVarResolverSession();
String s = method.getAnnotation(RestMethod.class).swagger().externalDocs();
if (s.isEmpty())
s = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".externalDocs");
if (s != null)
return p.parse(vr.resolve(s), ExternalDocumentation.class);
Operation o = getSwaggerOperationFromFile(method, req);
return o == null ? null : o.getExternalDocs();
}
/**
* Returns the localized parameter info for the specified java method.
*
* <p>
* Subclasses can override this method to provide their own parameter info.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>Operation information from swagger file.
* <li>{@link MethodSwagger#parameters() @MethodSwagger.parameters()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ja>@RestMethod</ja>(
* swagger=<ja>@MethodSwagger</ja>(
* parameters={
* <ja>@Parameter</ja>(in=<js>"path"</js>, name=<js>"a"</js>, description=<js>"The 'a' attribute"</js>)
* }
* )
* )
* <jk>public</jk> Object myMethod() {...}
*
* <cc>// Pulled from some other location</cc>
* <ja>@RestMethod</ja>(
* swagger=<ja>@MethodSwagger</ja>(
* parameters={
* <ja>@Parameter</ja>(in=<js>"path"</js>, name=<js>"a"</js>, description=<js>"$L{myLocalizedParamADescription}"</js>)
* }
* )
* )
* <jk>public</jk> Object myMethod() {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].[javaMethodName].parameters</ck>
* <li><ck>[javaMethodName].parameters</ck>
* </ol>
* <br>Value is a JSON representation of a <code>{@link ParameterInfo}[]</code> object.
* <br>Value can contain any SVL variables defined on the {@link MethodSwagger#parameters() @MethodSwagger.parameters()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.myMethod.parameters</ck> = <cv>[{name:"a",in:"path",description:"The ''a'' attribute"}]</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.myMethod.parameters</ck> = <cv>$C{MyStrings/MyClass.myMethod.parameters}</cv>
* </p>
* <li>Information gathered directly from the parameters on the Java method.
* </ol>
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return The localized parameter info of the method, or <jk>null</jk> if none was found.
* @throws Exception
*/
public List<ParameterInfo> getMethodParameters(Method method, RestRequest req) throws Exception {
Operation o = getSwaggerOperationFromFile(method, req);
if (o != null && o.getParameters() != null)
return o.getParameters();
VarResolverSession vr = req.getVarResolverSession();
JsonParser jp = JsonParser.DEFAULT;
Map<String,ParameterInfo> m = new TreeMap<>();
// First parse @RestMethod.parameters() annotation.
for (org.apache.juneau.rest.annotation.Parameter v : method.getAnnotation(RestMethod.class).swagger().parameters()) {
String in = vr.resolve(v.in());
ParameterInfo p = parameterInfo(in, vr.resolve(v.name()));
if (! v.description().isEmpty())
p.description(vr.resolve(v.description()));
if (v.required())
p.required(v.required());
if ("body".equals(in)) {
if (! v.schema().isEmpty())
p.schema(jp.parse(vr.resolve(v.schema()), SchemaInfo.class));
} else {
if (v.allowEmptyValue())
p.allowEmptyValue(v.allowEmptyValue());
if (! v.collectionFormat().isEmpty())
p.collectionFormat(vr.resolve(v.collectionFormat()));
if (! v._default().isEmpty())
p._default(vr.resolve(v._default()));
if (! v.format().isEmpty())
p.format(vr.resolve(v.format()));
if (! v.items().isEmpty())
p.items(jp.parse(vr.resolve(v.items()), Items.class));
p.type(vr.resolve(v.type()));
}
m.put(p.getIn() + '.' + p.getName(), p);
}
// Next, look in resource bundle.
String s = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".parameters");
if (s != null) {
for (ParameterInfo pi : jp.parse(vr.resolve(s), ParameterInfo[].class)) {
String key = pi.getIn() + '.' + pi.getName();
ParameterInfo p = m.get(key);
if (p == null)
m.put(key, pi);
else
p.copyFrom(pi);
}
}
// Finally, look for parameters defined on method.
for (RestParam mp : context.getRestParams(method)) {
RestParamType in = mp.getParamType();
if (in != RestParamType.OTHER) {
String k2 = in.toString() + '.' + (in == RestParamType.BODY ? null : mp.getName());
ParameterInfo p = m.get(k2);
if (p == null) {
p = parameterInfoStrict(in.toString(), mp.getName());
m.put(k2, p);
}
}
}
return m.isEmpty() ? null : new ArrayList<>(m.values());
}
/**
* Returns the localized response info for the specified java method.
*
* <p>
* Subclasses can override this method to provide their own parameter info.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>Operation information from swagger file.
* <li>{@link MethodSwagger#responses() @MethodSwagger.responses()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ja>@RestMethod</ja>(
* swagger=<ja>@MethodSwagger</ja>(
* responses={
* <ja>@Response</ja>(
* value=302,
* description=<js>"Thing wasn't found here"</js>,
* headers={
* <ja>@Parameter</ja>(name=<js>"Location"</js>, description=<js>"The place to find the thing"</js>)
* }
* )
* }
* )
* )
* <jk>public</jk> Object myMethod() {...}
*
* <cc>// Pulled from some other location</cc>
* <ja>@RestMethod</ja>(
* swagger=<ja>@MethodSwagger</ja>(
* responses={
* <ja>@Response</ja>(
* value=302,
* description=<js>"Thing wasn't found here"</js>,
* headers={
* <ja>@Parameter</ja>(name=<js>"Location"</js>, description=<js>"$L{myLocalizedResponseDescription}"</js>)
* }
* )
* }
* )
* )
* <jk>public</jk> Object myMethod() {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].[javaMethodName].responses</ck>
* <li><ck>[javaMethodName].responses</ck>
* </ol>
* <br>Value is a JSON representation of a <code>Map&lt;Integer,{@link ResponseInfo}&gt;</code> object.
* <br>Value can contain any SVL variables defined on the {@link MethodSwagger#responses() @MethodSwagger.responses()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.myMethod.responses</ck> = <cv>{302:{description:'Thing wasn''t found here',headers={Location:{description:"The place to find the thing"}}}</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.myMethod.responses</ck> = <cv>$C{MyStrings/MyClass.myMethod.responses}</cv>
* </p>
* <li>Information gathered directly from the parameters on the Java method.
* </ol>
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return The localized response info of the method, or <jk>null</jk> if none was found.
* @throws Exception
*/
@SuppressWarnings("unchecked")
public Map<Integer,ResponseInfo> getMethodResponses(Method method, RestRequest req) throws Exception {
Operation o = getSwaggerOperationFromFile(method, req);
if (o != null && o.getResponses() != null)
return o.getResponses();
VarResolverSession vr = req.getVarResolverSession();
JsonParser jp = JsonParser.DEFAULT;
Map<Integer,ResponseInfo> m = new TreeMap<>();
Map<String,HeaderInfo> m2 = new TreeMap<>();
// First parse @RestMethod.parameters() annotation.
for (Response r : method.getAnnotation(RestMethod.class).swagger().responses()) {
int httpCode = r.value();
String description = r.description().isEmpty() ? RestUtils.getHttpResponseText(r.value()) : vr.resolve(r.description());
ResponseInfo r2 = responseInfo(description);
if (r.headers().length > 0) {
for (org.apache.juneau.rest.annotation.Parameter v : r.headers()) {
HeaderInfo h = headerInfoStrict(vr.resolve(v.type()));
if (! v.collectionFormat().isEmpty())
h.collectionFormat(vr.resolve(v.collectionFormat()));
if (! v._default().isEmpty())
h._default(vr.resolve(v._default()));
if (! v.description().isEmpty())
h.description(vr.resolve(v.description()));
if (! v.format().isEmpty())
h.format(vr.resolve(v.format()));
if (! v.items().isEmpty())
h.items(jp.parse(vr.resolve(v.items()), Items.class));
r2.header(v.name(), h);
m2.put(httpCode + '.' + v.name(), h);
}
}
m.put(httpCode, r2);
}
// Next, look in resource bundle.
String s = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".responses");
if (s != null) {
for (Map.Entry<Integer,ResponseInfo> e : ((Map<Integer,ResponseInfo>)jp.parse(vr.resolve(s), Map.class, Integer.class, ResponseInfo.class)).entrySet()) {
Integer httpCode = e.getKey();
ResponseInfo ri = e.getValue();
ResponseInfo r = m.get(httpCode);
if (r == null)
m.put(httpCode, ri);
else
r.copyFrom(ri);
}
}
return m.isEmpty() ? null : m;
}
/**
* Returns the supported <code>Accept</code> types the specified Java method.
*
* <p>
* Subclasses can override this method to provide their own produces info.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link RestMethod#produces() @RestMethod.supportedAcceptTypes()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ja>@RestMethod</ja>(supportedAcceptTypes={<js>"text/json"</js>})
* <jk>public</jk> Object myMethod() {...}
*
* <cc>// Pulled from some other location</cc>
* <ja>@RestMethod</ja>(supportedAcceptTypes={<js>"$C{mySupportedProduces}"</js>})
* <jk>public</jk> Object myMethod() {...}
* </p>
* <li>Media types defined on the parsers associated with the method.
* </ol>
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return The supported <code>Accept</code> types of the method, or <jk>null</jk> if none was found
* or the list of media types match those of the parent resource class.
* @throws Exception
*/
public List<MediaType> getMethodProduces(Method method, RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
String[] s = method.getAnnotation(RestMethod.class).produces();
if (s.length > 0)
return Arrays.asList(MediaType.forStrings(vr.resolve(s)));
List<MediaType> l = req.getSerializers().getSupportedMediaTypes();
return (l.equals(context.getProduces()) ? null : l);
}
/**
* Returns the supported <code>Content-Type</code> types the specified Java method.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link RestMethod#consumes() @RestMethod.supportedContentTypes()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ja>@RestMethod</ja>(supportedContentTypes={<js>"text/json"</js>})
* <jk>public</jk> Object myMethod() {...}
*
* <cc>// Pulled from some other location</cc>
* <ja>@RestMethod</ja>(supportedContentTypes={<js>"$C{mySupportedConsumes}"</js>})
* <jk>public</jk> Object myMethod() {...}
* </p>
* <li>Media types defined on the serializers associated with the method.
* </ol>
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return The supported <code>Content-Type</code> types of the method, or <jk>null</jk> if none was found
* or the list of media types match those of the parent resource class.
* @throws Exception
*/
public List<MediaType> getMethodConsumes(Method method, RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
String[] s = method.getAnnotation(RestMethod.class).consumes();
if (s.length > 0)
return Arrays.asList(MediaType.forStrings(vr.resolve(s)));
List<MediaType> l = req.getParsers().getSupportedMediaTypes();
return (l.equals(context.getConsumes()) ? null : l);
}
/**
* Returns whether the specified method is deprecated
*
* <p>
* The default implementation returns the value from the following location:
* <ol class='spaced-list'>
* <li>{@link MethodSwagger#deprecated() @MethodSwagger.deprecated()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <ja>@RestMethod</ja>(
* swagger=<ja>@MethodSwagger</ja>(
* deprecated=<jk>true</jk>
* )
* )
* <jk>public</jk> Object myMethod() {...}
* </p>
* </ol>
*
* @param method The Java method annotated with {@link RestMethod @RestMethod}.
* @param req The current request.
* @return <jk>true</jk> if the method is deprecated.
* @throws Exception
*/
public boolean isDeprecated(Method method, RestRequest req) throws Exception {
return method.getAnnotation(RestMethod.class).swagger().deprecated();
}
/**
* Returns the localized site name of this REST resource.
*
* <p>
* Subclasses can override this method to provide their own site name.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link RestResource#siteName() @RestResource.siteName()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(siteName=<js>"My Site"</js>)
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(siteName=<js>"$L{myLocalizedSiteName}"</js>)
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].siteName</ck>
* <li><ck>siteName</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link RestResource#siteName() @RestResource.siteName()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.siteName</ck> = <cv>My Site</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.siteName</ck> = <cv>$C{MyStrings/MyClass.siteName}</cv>
* </p>
* </ol>
*
* @param req The current request.
* @return The localized site name of this REST resource, or <jk>null</jk> if none was found.
* @throws Exception
*/
@Override /* RestInfoProvider */
public String getSiteName(RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
if (siteName != null)
return vr.resolve(siteName);
String siteName = context.getMessages().findFirstString(req.getLocale(), "siteName");
if (siteName != null)
return vr.resolve(siteName);
return null;
}
/**
* Returns the localized title of this REST resource.
*
* <p>
* Subclasses can override this method to provide their own title.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link RestResource#title() @RestResource.siteName()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(title=<js>"My Resource"</js>)
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(title=<js>"$L{myLocalizedTitle}"</js>)
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].title</ck>
* <li><ck>title</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link RestResource#title() @RestResource.title()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.title</ck> = <cv>My Resource</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.title</ck> = <cv>$C{MyStrings/MyClass.title}</cv>
* </p>
* <li><ck>/info/title</ck> entry in swagger file.
* </ol>
*
* @param req The current request.
* @return The localized title of this REST resource, or <jk>null</jk> if none was found.
* @throws Exception
*/
@Override /* RestInfoProvider */
public String getTitle(RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
if (title != null)
return vr.resolve(title);
String title = context.getMessages().findFirstString(req.getLocale(), "title");
if (title != null)
return vr.resolve(title);
Swagger s = getSwaggerFromFile(req);
if (s != null && s.getInfo() != null)
return s.getInfo().getTitle();
return null;
}
/**
* Returns the localized description of this REST resource.
*
* <p>
* Subclasses can override this method to provide their own description.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link RestResource#description() @RestResource.description()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(description=<js>"My Resource"</js>)
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(description=<js>"$L{myLocalizedDescription}"</js>)
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].description</ck>
* <li><ck>description</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link RestResource#description() @RestResource.description()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.description</ck> = <cv>My Resource</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.description</ck> = <cv>$C{MyStrings/MyClass.description}</cv>
* </p>
* <li><ck>/info/description</ck> entry in swagger file.
* </ol>
*
* @param req The current request.
* @return The localized description of this REST resource, or <jk>null</jk> if none was was found.
* @throws Exception
*/
@Override /* RestInfoProvider */
public String getDescription(RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
if (description != null)
return vr.resolve(description);
String description = context.getMessages().findFirstString(req.getLocale(), "description");
if (description != null)
return vr.resolve(description);
Swagger s = getSwaggerFromFile(req);
if (s != null && s.getInfo() != null)
return s.getInfo().getDescription();
return null;
}
/**
* Returns the localized contact information of this REST resource.
*
* <p>
* Subclasses can override this method to provide their own contact information.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link ResourceSwagger#contact() @ResourceSwagger.contact()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(contact=<js>"{name:'John Smith',email:'john.smith@foo.bar'}"</js>)
* )
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(contact=<js>"$C{MyStrings/MyClass.myContactInfo}"</js>)
* )
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].contact</ck>
* <li><ck>contact</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link ResourceSwagger#contact() @ResourceSwagger.contact()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.contact</ck> = <cv>{name:"John Smith",email:"john.smith@foo.bar"}</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.contact</ck> = <cv>$C{MyStrings/MyClass.myContactInfo}</cv>
* </p>
* <li><ck>/info/contact</ck> entry in swagger file.
* </ol>
*
* @param req The current request.
* @return
* The localized contact information of this REST resource, or <jk>null</jk> if none was found.
* @throws Exception
*/
public Contact getContact(RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
JsonParser jp = JsonParser.DEFAULT;
try {
if (contact != null)
return jp.parse(vr.resolve(contact), Contact.class);
String contact = context.getMessages().findFirstString(req.getLocale(), "contact");
if (contact != null)
return jp.parse(vr.resolve(contact), Contact.class);
Swagger s = getSwaggerFromFile(req);
if (s != null && s.getInfo() != null)
return s.getInfo().getContact();
return null;
} catch (ParseException e) {
throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
}
}
/**
* Returns the localized license information of this REST resource.
*
* <p>
* Subclasses can override this method to provide their own license information.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link ResourceSwagger#license() @ResourceSwagger.license()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(license=<js>"{name:'Apache 2.0',url:'http://www.apache.org/licenses/LICENSE-2.0.html'}"</js>)
* )
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(license=<js>"$C{MyStrings/MyClass.myLicenseInfo}"</js>)
* )
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].license</ck>
* <li><ck>license</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link ResourceSwagger#license() @ResourceSwagger.license()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.license</ck> = <cv>{name:"Apache 2.0",url:"http://www.apache.org/licenses/LICENSE-2.0.html"}</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.license</ck> = <cv>$C{MyStrings/MyClass.myLicenseInfo}</cv>
* </p>
* <li><ck>/info/license</ck> entry in swagger file.
* </ol>
*
* @param req The current request.
* @return
* The localized license information of this REST resource, or <jk>null</jk> if none was found found.
* @throws Exception
*/
public License getLicense(RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
JsonParser jp = JsonParser.DEFAULT;
try {
if (license != null)
return jp.parse(vr.resolve(license), License.class);
String license = context.getMessages().findFirstString(req.getLocale(), "license");
if (license != null)
return jp.parse(vr.resolve(license), License.class);
Swagger s = getSwaggerFromFile(req);
if (s != null && s.getInfo() != null)
return s.getInfo().getLicense();
return null;
} catch (ParseException e) {
throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
}
}
/**
* Returns the terms-of-service information of this REST resource.
*
* <p>
* Subclasses can override this method to provide their own terms-of-service information.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link ResourceSwagger#termsOfService() @ResourceSwagger.termsOfService()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(termsOfService=<js>"You're on your own"</js>)
* )
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(termsOfService=<js>"$C{MyStrings/MyClass.myTermsOfService}"</js>)
* )
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].termsOfService</ck>
* <li><ck>termsOfService</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link ResourceSwagger#termsOfService() @ResourceSwagger.termsOfService()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.termsOfService</ck> = <cv>You''re on your own</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.termsOfService</ck> = <cv>$C{MyStrings/MyClass.myTermsOfService}</cv>
* </p>
* <li><ck>/info/termsOfService</ck> entry in swagger file.
* </ol>
*
* @param req The current request.
* @return
* The localized terms-of-service of this REST resource, or <jk>null</jk> if none was found.
* @throws Exception
*/
public String getTermsOfService(RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
if (termsOfService != null)
return vr.resolve(termsOfService);
String termsOfService = context.getMessages().findFirstString(req.getLocale(), "termsOfService");
if (termsOfService != null)
return vr.resolve(termsOfService);
Swagger s = getSwaggerFromFile(req);
if (s != null && s.getInfo() != null)
return s.getInfo().getTermsOfService();
return null;
}
/**
* Returns the version information of this REST resource.
*
* <p>
* Subclasses can override this method to provide their own version information.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link ResourceSwagger#version() @ResourceSwagger.version()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(version=<js>"2.0"</js>)
* )
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(version=<js>"$C{MyStrings/MyClass.myVersion}"</js>)
* )
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].version</ck>
* <li><ck>version</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link ResourceSwagger#version() @ResourceSwagger.version()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.version</ck> = <cv>2.0</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.version</ck> = <cv>$C{MyStrings/MyClass.myVersion}</cv>
* </p>
* <li><ck>/info/version</ck> entry in swagger file.
* </ol>
*
* @param req The current request.
* @return
* The localized version of this REST resource, or <jk>null</jk> if none was found.
* @throws Exception
*/
public String getVersion(RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
if (version != null)
return vr.resolve(version);
String version = context.getMessages().findFirstString(req.getLocale(), "version");
if (version != null)
return vr.resolve(version);
Swagger s = getSwaggerFromFile(req);
if (s != null && s.getInfo() != null)
return s.getInfo().getVersion();
return null;
}
/**
* Returns the supported <code>Content-Type</code> request headers for the REST resource.
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link ResourceSwagger#version() @ResourceSwagger.version()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(version=<js>"2.0"</js>)
* )
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(version=<js>"$C{MyStrings/MyClass.myVersion}"</js>)
* )
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].version</ck>
* <li><ck>version</ck>
* </ol>
* <br>Value can contain any SVL variables defined on the {@link ResourceSwagger#version() @ResourceSwagger.version()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.version</ck> = <cv>2.0</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.version</ck> = <cv>$C{MyStrings/MyClass.myVersion}</cv>
* </p>
* <li><ck>/info/version</ck> entry in swagger file.
* </ol>
*
* @param req The current request.
* @return
* The supported <code>Content-Type</code> request headers of the REST resource, or <jk>null</jk> if none were found.
* @throws Exception
*/
public List<MediaType> getConsumes(RestRequest req) throws Exception {
List<MediaType> l = req.getContext().getConsumes();
return l.isEmpty() ? null : l;
}
/**
* Returns the supported <code>Accept</code> request headers for the REST resource.
*
* @param req The current request.
* @return
* The supported <code>Accept</code> request headers of the REST resource, or <jk>null</jk> if none were found.
* @throws Exception
*/
public List<MediaType> getProduces(RestRequest req) throws Exception {
List<MediaType> l = req.getContext().getProduces();
return l.isEmpty() ? null : l;
}
/**
* Returns the version information of this REST resource.
*
* <p>
* Subclasses can override this method to provide their own version information.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link ResourceSwagger#tags() @ResourceSwagger.tags()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(tags=<js>"foo,bar,baz"</js>)
* )
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(tags=<js>"$C{MyStrings/MyClass.myTags}"</js>)
* )
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].tags</ck>
* <li><ck>tags</ck>
* </ol>
* <br>Value is either a comma-delimited list or a JSON array.
* <br>Value can contain any SVL variables defined on the {@link ResourceSwagger#tags() @ResourceSwagger.tags()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Comma-delimited list</cc>
* <ck>MyClass.tags</ck> = <cv>foo,bar,baz</cv>
*
* <cc>// JSON array</cc>
* <ck>MyClass.tags</ck> = <cv>["foo","bar","baz"]</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.tags</ck> = <cv>$C{MyStrings/MyClass.myTags}</cv>
* </p>
* <li><ck>tags</ck> entry in swagger file.
* </ol>
*
* @param req The current request.
* @return
* The localized tags of this REST resource, or <jk>null</jk> if none was found.
* @throws Exception
*/
public List<Tag> getTags(RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
JsonParser jp = JsonParser.DEFAULT;
try {
if (tags != null)
return jp.parse(vr.resolve(tags), ArrayList.class, Tag.class);
String tags = context.getMessages().findFirstString(req.getLocale(), "tags");
if (tags != null)
return jp.parse(vr.resolve(tags), ArrayList.class, Tag.class);
Swagger s = getSwaggerFromFile(req);
if (s != null && s.getTags() != null)
return s.getTags();
return null;
} catch (Exception e) {
throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
}
}
/**
* Returns the version information of this REST resource.
*
* <p>
* Subclasses can override this method to provide their own version information.
*
* <p>
* The default implementation returns the value from the following locations (whichever matches first):
* <ol class='spaced-list'>
* <li>{@link ResourceSwagger#externalDocs() @ResourceSwagger.externalDocs()} annotation on this class, and then any parent classes.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <jc>// Direct value</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(externalDocs=<js>"{url:'http://juneau.apache.org'}"</js>)
* )
* <jk>public class</jk> MyResource {...}
*
* <jc>// Pulled from some other location</jc>
* <ja>@RestResource</ja>(
* swagger=<ja>@ResourceSwagger</ja>(externalDocs=<js>"$C{MyStrings/MyClass.myExternalDocs}"</js>)
* )
* <jk>public class</jk> MyResource {...}
* </p>
* <li>Localized strings from resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
* on the resource class, then any parent classes.
* <ol>
* <li><ck>[ClassName].externalDocs</ck>
* <li><ck>externalDocs</ck>
* </ol>
* <br>Value is a JSON objec representation of a {@link ExternalDocumentation} object.
* <br>Value can contain any SVL variables defined on the {@link ResourceSwagger#externalDocs() @ResourceSwagger.externalDocs()} annotation.
* <h5 class='figure'>Examples:</h5>
* <p class='bcode'>
* <cc>// Direct value</cc>
* <ck>MyClass.externalDocs</ck> = <cv>{url:"http://juneau.apache.org"}</cv>
*
* <cc>// Pulled from some other location</cc>
* <ck>MyClass.externalDocs</ck> = <cv>$C{MyStrings/MyClass.myExternalDocs}</cv>
* </p>
* <li><ck>externalDocs</ck> entry in swagger file.
* </ol>
*
* @param req The current request.
* @return
* The localized external documentation of this REST resource, or <jk>null</jk> if none was found.
* @throws Exception
*/
public ExternalDocumentation getExternalDocs(RestRequest req) throws Exception {
VarResolverSession vr = req.getVarResolverSession();
JsonParser jp = JsonParser.DEFAULT;
try {
if (externalDocs != null)
return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
String externalDocs = context.getMessages().findFirstString(req.getLocale(), "externalDocs");
if (externalDocs != null)
return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
Swagger s = getSwaggerFromFile(req);
if (s != null)
return s.getExternalDocs();
return null;
} catch (Exception e) {
throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
}
}
private Operation getSwaggerOperationFromFile(Method method, RestRequest req) throws Exception {
Swagger s = getSwaggerFromFile(req);
if (s != null) {
Map<String,Map<String,Operation>> sp = s.getPaths();
if (sp != null) {
Map<String,Operation> spp = sp.get(method.getAnnotation(RestMethod.class).path());
if (spp != null)
return spp.get(req.getMethod());
}
}
return null;
}
}