DS-3489: Move DSpaceResource links to LinkFactory

This commit is contained in:
Tom Desair
2017-10-26 15:51:16 +02:00
committed by Tom Desair
parent a6d8808c49
commit c96886b598
7 changed files with 159 additions and 145 deletions

View File

@@ -2,14 +2,19 @@
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
* <p>
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest;
import java.io.File;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.Filter;
import org.dspace.app.rest.filter.DSpaceRequestContextFilter;
import org.dspace.app.rest.model.hateoas.DSpaceRelProvider;
import org.dspace.app.rest.parameter.resolver.SearchFilterResolver;
import org.dspace.app.rest.utils.ApplicationConfig;
import org.dspace.app.util.DSpaceContextListener;
import org.dspace.kernel.DSpaceKernel;
@@ -28,11 +33,12 @@ import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.annotation.Order;
import org.springframework.hateoas.RelProvider;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@@ -59,16 +65,12 @@ import java.io.File;
*/
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
private static final Logger log = LoggerFactory.getLogger(Application.class);
@Autowired
private ApplicationConfig configuration;
@Value("${dspace.dir}")
private String dspaceHome;
private transient DSpaceKernelImpl kernelImpl;
/**
* Override the default SpringBootServletInitializer.configure() method,
* passing it this Application class.
@@ -84,6 +86,9 @@ public class Application extends SpringBootServletInitializer {
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class)
.initializers(new DSpaceKernelInitializer());
}
// start the kernel when the webapp starts
try {
@@ -112,14 +117,7 @@ public class Application extends SpringBootServletInitializer {
@Bean
public ServletContextInitializer contextInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext)
throws ServletException {
servletContext.setInitParameter("dspace.dir", configuration.getDspaceHome());
}
};
return servletContext -> servletContext.setInitParameter("dspace.dir", configuration.getDspaceHome());
}
/**
@@ -171,7 +169,7 @@ public class Application extends SpringBootServletInitializer {
}
@Bean
public WebMvcConfigurer webMvcConfigurer() {
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
@@ -180,14 +178,61 @@ public class Application extends SpringBootServletInitializer {
registry.addMapping("/api/**").allowedOrigins(corsAllowedOrigins);
}
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new SearchFilterResolver());
}
};
}
/** Utility class that will destroy the DSpace Kernel on Spring Boot shutdown */
private class DSpaceKernelDestroyer implements ApplicationListener<ContextClosedEvent> {
private DSpaceKernelImpl kernelImpl;
public DSpaceKernelDestroyer(DSpaceKernelImpl kernelImpl) {
this.kernelImpl = kernelImpl;
}
public void onApplicationEvent(final ContextClosedEvent event) {
if (this.kernelImpl != null) {
this.kernelImpl.destroy();
this.kernelImpl = null;
}
}
}
/** Utility class that will initialize the DSpace Kernel on Spring Boot startup */
private class DSpaceKernelInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private transient DSpaceKernelImpl kernelImpl;
public void initialize(final ConfigurableApplicationContext applicationContext) {
String dspaceHome = applicationContext.getEnvironment().getProperty("dspace.dir");
// start the kernel when the webapp starts
try {
this.kernelImpl = DSpaceKernelInit.getKernel(null);
if (!this.kernelImpl.isRunning()) {
this.kernelImpl.start(getProvidedHome(dspaceHome)); // init the kernel
}
//Set the DSpace Kernel Application context as a parent of the Spring Boot context so that
//we can auto-wire all DSpace Kernel services
applicationContext.setParent(kernelImpl.getServiceManager().getApplicationContext());
//Add a listener for Spring Boot application shutdown so that we can nicely cleanup the DSpace kernel.
applicationContext.addApplicationListener(new DSpaceKernelDestroyer(kernelImpl));
} catch (Exception e) {
// failed to start so destroy it and log and throw an exception
try {
this.kernelImpl.destroy();
} catch (Exception e1) {
// nothing
}
String message = "Failure during ServletContext initialisation: " + e.getMessage();
log.error(message + ":" + e.getMessage(), e);
throw new RuntimeException(message, e);
}
}
/**
* Find DSpace's "home" directory.
* Initially look for JNDI Resource called "java:/comp/env/dspace.dir".
@@ -210,23 +255,8 @@ public class Application extends SpringBootServletInitializer {
providedHome = dspaceHome;
}
}
return providedHome;
}
return providedHome;
}
private class DSpaceKernelDestroyer implements ApplicationListener<ContextClosedEvent> {
private DSpaceKernelImpl kernelImpl;
public DSpaceKernelDestroyer(DSpaceKernelImpl kernelImpl) {
this.kernelImpl = kernelImpl;
}
public void onApplicationEvent(final ContextClosedEvent event) {
if (this.kernelImpl != null) {
this.kernelImpl.destroy();
this.kernelImpl = null;
}
}
}
}

View File

@@ -26,6 +26,7 @@ import org.dspace.app.rest.exception.PaginationException;
import org.dspace.app.rest.exception.RepositoryNotFoundException;
import org.dspace.app.rest.exception.RepositorySearchMethodNotFoundException;
import org.dspace.app.rest.exception.RepositorySearchNotFoundException;
import org.dspace.app.rest.link.HalLinkService;
import org.dspace.app.rest.model.LinkRest;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
@@ -74,6 +75,9 @@ public class RestResourceController implements InitializingBean {
@Autowired
RestRepositoryUtils repositoryUtils;
@Autowired
HalLinkService linkService;
@Override
public void afterPropertiesSet() {
List<Link> links = new ArrayList<Link>();
@@ -124,6 +128,7 @@ public class RestResourceController implements InitializingBean {
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + id + " not found");
}
DSpaceResource result = repository.wrapResource(modelObject);
linkService.addLinks(result);
return result;
}
@@ -213,6 +218,8 @@ public class RestResourceController implements InitializingBean {
}
RestModel modelObject = repository.findOne(uuid);
DSpaceResource result = repository.wrapResource(modelObject, rel);
linkService.addLinks(result);
if (result.getLink(rel) == null) {
// TODO create a custom exception
throw new ResourceNotFoundException(rel + "undefined for " + model);
@@ -251,6 +258,7 @@ public class RestResourceController implements InitializingBean {
Page<DSpaceResource<T>> resources;
try {
resources = repository.findAll(page).map(repository::wrapResource);
resources.forEach(linkService::addLinks);
} catch (PaginationException pe) {
resources = new PageImpl<DSpaceResource<T>>(new ArrayList<DSpaceResource<T>>(), page, pe.getTotal());
}
@@ -320,6 +328,7 @@ public class RestResourceController implements InitializingBean {
ResourceSupport result = null;
if (returnPage) {
Page<DSpaceResource<T>> resources = ((Page<T>) searchResult).map(repository::wrapResource);
resources.forEach(linkService::addLinks);
result = assembler.toResource(resources, link);
} else {
result = repository.wrapResource((T) searchResult);

View File

@@ -7,38 +7,75 @@
*/
package org.dspace.app.rest.link;
import org.dspace.app.rest.DiscoveryRestController;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.dspace.app.rest.model.hateoas.SearchConfigurationResource;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.stereotype.Component;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.RestResourceController;
import org.dspace.app.rest.model.LinkRest;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.utils.Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
/**
* Created by raf on 25/09/2017.
*/
@Component
public class DSpaceResourceHalLinkFactory extends HalLinkFactory<DSpaceResource, DiscoveryRestController> {
public class DSpaceResourceHalLinkFactory extends HalLinkFactory<DSpaceResource, RestResourceController> {
@Autowired
private Utils utils;
protected void addLinks(DSpaceResource halResource, LinkedList<Link> list) {
RestModel data = halResource.getData();
try {
for (PropertyDescriptor pd : Introspector.getBeanInfo(data.getClass()).getPropertyDescriptors()) {
Method readMethod = pd.getReadMethod();
String name = pd.getName();
if (readMethod != null && !"class".equals(name)) {
LinkRest linkAnnotation = readMethod.getAnnotation(LinkRest.class);
if (linkAnnotation != null) {
if (StringUtils.isNotBlank(linkAnnotation.name())) {
name = linkAnnotation.name();
}
protected Class<DiscoveryRestController> getControllerClass() {
return null;
Link linkToSubResource = utils.linkToSubResource(data, name);
// no method is specified to retrieve the linked object(s) so check if it is already here
if (StringUtils.isBlank(linkAnnotation.method())) {
halResource.add(linkToSubResource);
}
} else if (RestModel.class.isAssignableFrom(readMethod.getReturnType())) {
Link linkToSubResource = utils.linkToSubResource(data, name);
halResource.add(linkToSubResource);
}
}
}
} catch (IntrospectionException e) {
e.printStackTrace();
}
halResource.add(utils.linkToSingleResource(data, Link.REL_SELF));
}
protected Class<RestResourceController> getControllerClass() {
return RestResourceController.class;
}
protected Class<DSpaceResource> getResourceClass() {
return null;
return DSpaceResource.class;
}
protected String getSelfLink(DSpaceResource halResource) {
return null;
return utils.linkToSingleResource(halResource.getData(), Link.REL_SELF).getHref();
}
}

View File

@@ -7,18 +7,17 @@
*/
package org.dspace.app.rest.link;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
import java.util.LinkedList;
import java.util.List;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
/**
* Created by raf on 25/09/2017.
*/
@@ -26,7 +25,7 @@ import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
public abstract class HalLinkFactory<RESOURCE, CONTROLLER> {
public boolean supports(Class clazz) {
if(Objects.equals(clazz, getResourceClass())){
if(getResourceClass().isAssignableFrom(clazz)){
return true;
}
return false;

View File

@@ -1,44 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.rest.link;
import org.dspace.app.rest.DiscoveryRestController;
import org.dspace.app.rest.model.hateoas.DSpaceResource;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.dspace.app.rest.model.hateoas.ItemResource;
import org.dspace.app.rest.model.hateoas.SearchConfigurationResource;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.stereotype.Component;
import java.util.LinkedList;
import java.util.List;
/**
* Created by raf on 25/09/2017.
*/
@Component
public class ItemResourceHalLinkFactory extends HalLinkFactory<ItemResource, DiscoveryRestController> {
protected void addLinks(ItemResource halResource, LinkedList<Link> list) {
}
protected Class<DiscoveryRestController> getControllerClass() {
return null;
}
protected Class<ItemResource> getResourceClass() {
return null;
}
protected String getSelfLink(ItemResource halResource) {
return null;
}
}

View File

@@ -7,21 +7,13 @@
*/
package org.dspace.app.rest.link.search;
import java.util.LinkedList;
import org.dspace.app.rest.DiscoveryRestController;
import org.dspace.app.rest.link.HalLinkFactory;
import org.dspace.app.rest.model.SearchConfigurationRest;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.dspace.app.rest.model.hateoas.SearchConfigurationResource;
import org.dspace.app.rest.model.hateoas.SearchSupportResource;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.LinkedList;
import java.util.List;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
/**
* Created by raf on 25/09/2017.
@@ -32,7 +24,7 @@ public class SearchSupportHalLinkFactory extends HalLinkFactory<SearchSupportRes
protected void addLinks(SearchSupportResource halResource, LinkedList<Link> list) {
list.add(buildLink(Link.REL_SELF, getMethodOn()
.getSearchSupport(null, null)));
list.add(buildLink("configuration", getMethodOn().getSearchConfiguration(null, null)));
list.add(buildLink("search", getMethodOn().getSearchConfiguration(null, null)));
list.add(buildLink("facets", getMethodOn().getFacetsConfiguration(null, null)));
}

View File

@@ -7,6 +7,16 @@
*/
package org.dspace.app.rest.model.hateoas;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import org.apache.commons.lang3.StringUtils;
import org.dspace.app.rest.model.BaseObjectRest;
@@ -20,16 +30,6 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.hateoas.Link;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* A base class for DSpace Rest HAL Resource. The HAL Resource wraps the REST
* Resource adding support for the links and embedded resources. Each property
@@ -99,7 +99,6 @@ public abstract class DSpaceResource<T extends RestModel> extends HALResource {
Link linkToSubResource = utils.linkToSubResource(data, name);
// no method is specified to retrieve the linked object(s) so check if it is already here
if (StringUtils.isBlank(linkAnnotation.method())) {
Object linkedObject = readMethod.invoke(data);
Object wrapObject = linkedObject;
if (linkedObject instanceof RestModel) {
@@ -168,8 +167,6 @@ public abstract class DSpaceResource<T extends RestModel> extends HALResource {
}
}
else if (RestModel.class.isAssignableFrom(readMethod.getReturnType())) {
Link linkToSubResource = utils.linkToSubResource(data, name);
this.add(linkToSubResource);
RestModel linkedObject = (RestModel) readMethod.invoke(data);
if (linkedObject != null) {
embedded.put(name,
@@ -188,15 +185,9 @@ public abstract class DSpaceResource<T extends RestModel> extends HALResource {
| InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
}
this.add(utils.linkToSingleResource(data, Link.REL_SELF));
}
}
@Override
public void add(Link link) {
System.out.println("Chiamato "+link.getRel());
super.add(link);
}
public Map<String, Object> getEmbedded() {
return embedded;
}