Skip to content

Commit

Permalink
add spring boot starter. refactoring for easier controller config
Browse files Browse the repository at this point in the history
  • Loading branch information
abuttaro committed Feb 26, 2018
1 parent c5f8520 commit 8bcc3ae
Show file tree
Hide file tree
Showing 55 changed files with 1,325 additions and 377 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
<module>up-repository-jpa</module>
<module>up-http</module>
<module>up-spring-mvc</module>
<module>up-spring-boot-starter</module>
</modules>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.github.restup.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Optional tag annotation for resources, which may aid component scanning.
*
* @author abuttaro
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Resource {

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import com.github.restup.controller.model.ParsedResourceControllerRequest;
import com.github.restup.controller.model.ResourceControllerRequest;
import com.github.restup.controller.model.ResourceControllerResponse;
import com.github.restup.controller.request.parser.RequestParamParser;
import com.github.restup.controller.request.parser.RequestParser;
import com.github.restup.controller.settings.ControllerSettings;
import com.github.restup.errors.RequestError;
Expand All @@ -38,7 +37,6 @@
import com.github.restup.registry.settings.ControllerMethodAccess;
import com.github.restup.service.model.request.RequestObjectFactory;
import com.github.restup.util.Assert;
import com.google.gson.Gson;

/**
* <ol>
Expand Down Expand Up @@ -153,7 +151,7 @@ public class ResourceController {

private final static Logger log = LoggerFactory.getLogger(ResourceController.class);
private final ResourceRegistry registry;
private final ContentNegotiator[] contentNegotiators;
private final ContentNegotiator contentNegotiator;
private final RequestParser requestParser;
private final RequestInterceptor interceptor;
private final ExceptionHandler exceptionHandler;
Expand All @@ -173,7 +171,7 @@ public ResourceController(ControllerSettings settings) {
Assert.notNull(settings, "settings are required");

this.registry = settings.getRegistry();
this.contentNegotiators = settings.getContentNegotiators();
this.contentNegotiator = settings.getContentNegotiator();
this.requestParser = settings.getRequestParser();
this.interceptor = settings.getRequestInterceptor();
this.exceptionHandler = settings.getExceptionHandler();
Expand Down Expand Up @@ -366,7 +364,7 @@ <T, ID extends Serializable> Object requestInternal(ResourceControllerRequest re
boolean itemOperation = ids == 1;

// confirm content type is supported ahead of parsing work
ContentNegotiator contentNegotiator = getContentNegotiator(request);
accept(request);

MethodController<T, ID> methodController = getMethodController(request, access, itemOperation);

Expand All @@ -386,20 +384,13 @@ <T, ID extends Serializable> Object requestInternal(ResourceControllerRequest re
return contentNegotiator.formatResponse(parsedRequest, response, result);
}

/**
* @return ContentNegotiator for content type passed
* @throws RequestErrorException if content type is not supported
*/
private ContentNegotiator getContentNegotiator(ResourceControllerRequest request) {
for (ContentNegotiator contentNegotiator : contentNegotiators) {
if (contentNegotiator.accept(request)) {
return contentNegotiator;
}
private void accept(ResourceControllerRequest request) {
if (!contentNegotiator.accept(request)) {
throw RequestError.builder(request.getResource())
.status(StatusCode.UNSUPPORTED_MEDIA_TYPE)
.meta(ContentTypeNegotiation.CONTENT_TYPE, request.getContentType())
.buildException();
}
throw RequestError.builder(request.getResource())
.status(StatusCode.UNSUPPORTED_MEDIA_TYPE)
.meta(ContentTypeNegotiation.CONTENT_TYPE, request.getContentType())
.buildException();
}

@SuppressWarnings({"rawtypes"})
Expand Down Expand Up @@ -458,8 +449,13 @@ public Builder registry(ResourceRegistry registry) {
return me();
}

public Builder contentNegotiators(ContentNegotiator... contentNegotiator) {
settings.contentNegotiators(contentNegotiator);
public Builder contentNegotiator(ContentNegotiator contentNegotiator) {
settings.contentNegotiator(contentNegotiator);
return me();
}

public Builder contentNegotiator(ContentNegotiator.Builder contentNegotiator) {
settings.contentNegotiator(contentNegotiator);
return me();
}

Expand All @@ -468,13 +464,13 @@ public Builder interceptors(RequestInterceptor... interceptors) {
return me();
}

public Builder requestParsers(RequestParser... requestParsers) {
settings.requestParsers(requestParsers);
public Builder requestParser(RequestParser requestParser) {
settings.requestParser(requestParser);
return me();
}

public Builder requestParamParsers(RequestParamParser... requestParamParsers) {
settings.requestParamParsers(requestParamParsers);
public Builder requestParser(RequestParser.Builder requestParser) {
settings.requestParser(requestParser);
return me();
}

Expand All @@ -483,11 +479,6 @@ public Builder exceptionHandler(ExceptionHandler exceptionHandler) {
return me();
}

public Builder relationshipParser(RequestParser relationshipParser) {
settings.relationshipParser(relationshipParser);
return me();
}

public Builder linkBuilderFactory(LinkBuilderFactory linkBuilderFactory) {
settings.linkBuilderFactory(linkBuilderFactory);
return me();
Expand All @@ -503,11 +494,6 @@ public Builder jacksonObjectMapper(ObjectMapper mapper) {
return me();
}

public Builder gson(Gson gson) {
settings.gson(gson);
return me();
}

public Builder autoDetectDisabled(boolean b) {
settings.autoDetectDisabled(b);
return me();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package com.github.restup.controller.content.negotiation;

import org.apache.commons.lang3.ArrayUtils;
import com.github.restup.controller.linking.LinkBuilderFactory;
import com.github.restup.controller.linking.discovery.ServiceDiscovery;
import com.github.restup.controller.model.ParsedResourceControllerRequest;
import com.github.restup.controller.model.ResourceControllerRequest;
import com.github.restup.controller.model.ResourceControllerResponse;
import com.github.restup.controller.settings.BuilderSettingsCaptor;
import com.github.restup.registry.settings.AutoDetectConstants;

/**
* Negotiates return types based upon request details. <p> While this may query serialization using libraries such as Jackson or Gson, it is also likely that the object types and structure may have to change to modify the response structure depending upon the request context.
Expand Down Expand Up @@ -31,4 +36,71 @@ public interface ContentNegotiator {
*/
<T> Object formatResponse(ParsedResourceControllerRequest<T> request, ResourceControllerResponse response, Object result);


static Builder builder() {
return new Builder();
}

static class Builder {

private ContentNegotiator[] contentNegotiators;
private BuilderSettingsCaptor settingsCaptor;

Builder() {
super();
this.settingsCaptor = new BuilderSettingsCaptor();
}

Builder me() {
return this;
}

public Builder contentNegotiators(ContentNegotiator... contentNegotiators) {
this.contentNegotiators = contentNegotiators;
return me();
}

public Builder autoDetectDisabled(boolean autoDetectDisabled) {
settingsCaptor.setAutoDetectDisabled(autoDetectDisabled);
return me();
}

public Builder defaultMediaType(String mediaType) {
settingsCaptor.setDefaultMediaType(mediaType);
return me();
}

public Builder serviceDiscovery(ServiceDiscovery serviceDiscovery) {
settingsCaptor.setServiceDiscovery(serviceDiscovery);
return me();
}

public Builder linkBuilderFactory(LinkBuilderFactory linkBuilderFactory) {
settingsCaptor.setLinkBuilderFactory(linkBuilderFactory);
return me();
}

public Builder capture(BuilderSettingsCaptor settingsCaptor) {
this.settingsCaptor = settingsCaptor.capture(this.settingsCaptor);
return me();
}

public ContentNegotiator build() {
settingsCaptor.build();

ContentNegotiator[] arr = contentNegotiators;
if (!settingsCaptor.getAutoDetectDisabled()) {
if (AutoDetectConstants.JACKSON2_EXISTS) {
arr = ArrayUtils.addAll(arr, new JsonContentNegotiator(), new JsonApiContentNegotiator(settingsCaptor.getLinkBuilderFactory()));
}
}
if (settingsCaptor.getDefaultMediaType() != null) {
DefaultContentNegotiator defaultContentNegotiator = new DefaultContentNegotiator(settingsCaptor.getDefaultMediaType(), arr);
arr = ArrayUtils.add(arr, defaultContentNegotiator);
}
return new ContentNegotiatorChain(arr);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.github.restup.controller.content.negotiation;

import com.github.restup.controller.model.ParsedResourceControllerRequest;
import com.github.restup.controller.model.ResourceControllerRequest;
import com.github.restup.controller.model.ResourceControllerResponse;

public class ContentNegotiatorChain implements ContentNegotiator {

private final ContentNegotiator[] contentNegotiators;

public ContentNegotiatorChain(ContentNegotiator... contentNegotiators) {
this.contentNegotiators = contentNegotiators;
}

@Override
public <T> boolean accept(ResourceControllerRequest request) {
return null != getContentNegotiator(request);
}

@Override
public <T> Object formatResponse(ParsedResourceControllerRequest<T> request, ResourceControllerResponse response, Object result) {
return getContentNegotiator(request).formatResponse(request, response, result);
}

/**
* @return ContentNegotiator for content type passed
* @throws RequestErrorException if content type is not supported
*/
private ContentNegotiator getContentNegotiator(ResourceControllerRequest request) {
for (ContentNegotiator contentNegotiator : contentNegotiators) {
if (contentNegotiator.accept(request)) {
return contentNegotiator;
}
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.restup.controller.linking;

import com.github.restup.controller.linking.discovery.ServiceDiscovery;
import com.github.restup.controller.model.ParsedResourceControllerRequest;
import com.github.restup.controller.settings.ControllerSettings;

/**
* A factory for providing {@link LinkBuilder} implementations which may be configured using {@link ControllerSettings.Builder#linkBuilderFactory(LinkBuilderFactory)}
Expand All @@ -19,4 +19,8 @@ public interface LinkBuilderFactory {
*/
LinkBuilder getLinkBuilder(ParsedResourceControllerRequest<?> request, Object result);

static LinkBuilderFactory getDefaultLinkBuilderFactory(ServiceDiscovery discovery) {
return new DefaultLinkBuilderFactory(discovery);
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.github.restup.controller.linking.discovery;

import com.github.restup.controller.model.ParsedResourceControllerRequest;
import com.github.restup.registry.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.github.restup.controller.model.ParsedResourceControllerRequest;
import com.github.restup.registry.Resource;

/**
* A simple Map backed cache of discovered service urls.
Expand All @@ -18,7 +18,11 @@ public CachedServiceDiscovery(ServiceDiscovery delegate, Map<Resource<?,?>, Stri
this.cache = cache;
}

public CachedServiceDiscovery(ServiceDiscovery delegate) {
public static ServiceDiscovery cache(ServiceDiscovery serviceDiscovery) {
return new CachedServiceDiscovery(serviceDiscovery);
}

CachedServiceDiscovery(ServiceDiscovery delegate) {
this(delegate, new ConcurrentHashMap<>());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/**
* Default implementation builds urls based upon request &amp; registry details
*/
public class DefaultServiceDiscovery implements ServiceDiscovery {
class DefaultServiceDiscovery implements ServiceDiscovery {

@Override
public String locateResourceUrl(ParsedResourceControllerRequest<?> request, Resource<?, ?> resource) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ public interface ServiceDiscovery {
*/
String locateResourceUrl(ParsedResourceControllerRequest<?> request, Resource<?, ?> resource);

static ServiceDiscovery getDefaultServiceDiscovery() {
return new DefaultServiceDiscovery();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.restup.controller.request.parser;

import java.util.List;
import com.github.restup.controller.model.ParsedResourceControllerRequest;
import com.github.restup.controller.model.ResourceControllerRequest;
import com.github.restup.controller.request.parser.params.FieldsParser;
Expand All @@ -10,7 +11,6 @@
import com.github.restup.controller.request.parser.params.PageOffsetParser;
import com.github.restup.controller.request.parser.params.SortParamParser;
import com.github.restup.util.Assert;
import java.util.List;

/**
* Iterates over all parameter names executing the first {@link RequestParamParser} that accepts the parameter for each
Expand All @@ -21,6 +21,15 @@ public class ParameterParserChain implements RequestParser {

private final RequestParamParser[] parsers;


public static ParameterParserChain of(List<RequestParamParser> parsers) {
return of(parsers.toArray(new RequestParamParser[parsers.size()]));
}

public static ParameterParserChain of(RequestParamParser... parsers) {
return new ParameterParserChain(parsers);
}

public ParameterParserChain(RequestParamParser... parsers) {
super();
Assert.notEmpty("parsers are required", parsers);
Expand Down
Loading

0 comments on commit 8bcc3ae

Please sign in to comment.