com.mercateo.common.rest.schemagen.link.LinkCreator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common.rest.schemagen Show documentation
Show all versions of common.rest.schemagen Show documentation
Jersey add-on for dynamic link and schema building
package com.mercateo.common.rest.schemagen.link;
import com.google.common.collect.Iterables;
import com.googlecode.gentyref.GenericTypeReflector;
import com.mercateo.common.rest.schemagen.JsonSchemaGenerator;
import com.mercateo.common.rest.schemagen.link.relation.Relation;
import javax.ws.rs.BeanParam;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.Link.Builder;
import javax.ws.rs.core.UriBuilder;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
import static com.mercateo.common.rest.schemagen.link.helper.ParameterAnnotationVisitor.visitAnnotations;
import static java.util.Objects.requireNonNull;
public class LinkCreator {
public static final String TARGET_SCHEMA_PARAM_KEY = "targetSchema";
public static final String SCHEMA_PARAM_KEY = "schema";
public static final String METHOD_PARAM_KEY = "method";
private final LinkFactoryContext linkFactoryContext;
LinkCreator(LinkFactoryContext linkFactoryContext) {
this.linkFactoryContext = requireNonNull(linkFactoryContext);
}
public static Builder setRelation(Relation relation, URI uri) {
requireNonNull(relation);
requireNonNull(uri);
Builder builder = Link.fromUri(uri).rel(relation.getName());
if (requireNonNull(relation).getType().isShouldBeSerialized()) {
builder.param("relType", relation.getType().getName());
builder.param("target", relation.getType().getSerializedName());
}
return builder;
}
/**
* create a link for a resource method
*
* @param scopes list of Scope objects for every scope level
* @param relation relation of method
* @return link with schema if applicable
*/
public Link createFor(List scopes, Relation relation) {
final Class> resourceClass = scopes.get(0).getInvokedClass();
UriBuilder uriBuilder = UriBuilder.fromResource(resourceClass);
Map pathParameters = new HashMap<>();
for (Scope scope : scopes) {
final Method method = scope.getInvokedMethod();
final Object[] parameters = scope.getParams();
if (method.isAnnotationPresent(Path.class)) {
uriBuilder.path(method.getDeclaringClass(), method.getName());
}
pathParameters.putAll(collectPathParameters(scope, parameters));
setQueryParameters(uriBuilder, scope, parameters);
}
final URI uri = uriBuilder.buildFromMap(pathParameters);
Builder builder = setRelation(relation, uri);
addLinkProperties(scopes, builder);
detectMediaType(scopes, builder);
final Scope lastScopedMethod = Iterables.getLast(scopes);
addHttpMethod(builder, lastScopedMethod);
addSchemaIfNeeded(builder, lastScopedMethod);
if (linkFactoryContext.getBaseUri() != null) {
builder.baseUri(linkFactoryContext.getBaseUri());
}
return builder.build();
}
private void addLinkProperties(List scopes, Builder builder) {
final LinkProperties properties = Iterables.getLast(scopes).getInvokedMethod()
.getAnnotation(LinkProperties.class);
if (properties != null) {
Stream.of(properties.value()).forEach(x -> builder.param(x.key(), x.value()));
}
}
private void detectMediaType(Collection scopes, Builder builder) {
final Method method = Iterables.getLast(scopes).getInvokedMethod();
Optional.ofNullable(method.getAnnotation(Produces.class)).ifPresent(produces -> {
final String[] values = produces.value();
if (values.length > 0) {
builder.param("mediaType", values[0]);
}
});
}
private Map collectPathParameters(Scope scope,
Object[] parameters) {
final Map pathParameters = new HashMap<>();
visitAnnotations((parameter, parameterIndex, annotation) -> {
if (annotation instanceof PathParam) {
PathParam pathParamAnnotation = (PathParam) annotation;
pathParameters.put(pathParamAnnotation.value(), parameter);
} else if (annotation instanceof BeanParam) {
BeanParamExtractor beanParamExtractor = new BeanParamExtractor();
pathParameters.putAll(beanParamExtractor.getPathParameters(parameter));
}
}, scope.getInvokedMethod(), parameters);
return pathParameters;
}
private void setQueryParameters(final UriBuilder uriBuilder, Scope scope,
Object[] parameters) {
Type[] realParamTypes = GenericTypeReflector.getExactParameterTypes(scope
.getInvokedMethod(), scope.getInvokedClass());
visitAnnotations((parameter, parameterIndex, annotation) -> {
if (annotation instanceof QueryParam && parameter != null) {
uriBuilder.queryParam(((QueryParam) annotation).value(), parameter.toString());
} else if (annotation instanceof BeanParam && parameter != null) {
if (realParamTypes[parameterIndex] instanceof Class>) {
BeanParamExtractor beanParamExtractor = new BeanParamExtractor();
Map queryParameter = beanParamExtractor.getQueryParameters(
parameter);
queryParameter.forEach((uriBuilder::queryParam));
}
}
}, scope.getInvokedMethod(), parameters);
}
private void addHttpMethod(Builder builder, Scope scope) {
final List> httpMethodAnnotations = Arrays.asList(GET.class,
POST.class, PUT.class, DELETE.class);
final Method invokedMethod = scope.getInvokedMethod();
final Optional> httpMethod = httpMethodAnnotations.stream()
.filter(invokedMethod::isAnnotationPresent).findFirst();
if (httpMethod.isPresent()) {
builder.param(METHOD_PARAM_KEY, httpMethod.get().getSimpleName());
} else {
throw new IllegalArgumentException(
"LinkCreator: The method has to be annotated with one of: " + String.join(", ",
(Iterable) httpMethodAnnotations.stream().map(
Class::getSimpleName).map(m -> '@' + m)::iterator));
}
}
private void addSchemaIfNeeded(Builder builder, Scope method) {
final JsonSchemaGenerator schemaGenerator = linkFactoryContext.getSchemaGenerator();
Optional optionalInputSchema = schemaGenerator.createInputSchema(method,
linkFactoryContext.getFieldCheckerForSchema());
if (optionalInputSchema.isPresent()) {
builder.param(SCHEMA_PARAM_KEY, optionalInputSchema.get());
}
Optional optionalOutputSchema = schemaGenerator.createOutputSchema(method,
linkFactoryContext.getFieldCheckerForSchema());
if (optionalOutputSchema.isPresent()) {
builder.param(TARGET_SCHEMA_PARAM_KEY, optionalOutputSchema.get());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy