diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/Application.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/Application.java index c810835391..892bc7afa7 100644 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/Application.java +++ b/dspace-spring-rest/src/main/java/org/dspace/app/rest/Application.java @@ -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 - *

+ * * 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,41 +178,10 @@ public class Application extends SpringBootServletInitializer { registry.addMapping("/api/**").allowedOrigins(corsAllowedOrigins); } } - - @Override - public void addArgumentResolvers(List argumentResolvers) { - argumentResolvers.add(new SearchFilterResolver()); - } }; } - /** - * Find DSpace's "home" directory. - * Initially look for JNDI Resource called "java:/comp/env/dspace.dir". - * If not found, look for "dspace.dir" initial context parameter. - */ - private String getProvidedHome(String dspaceHome) { - String providedHome = null; - try { - Context ctx = new InitialContext(); - providedHome = (String) ctx.lookup("java:/comp/env/" + DSpaceConfigurationService.DSPACE_HOME); - } catch (Exception e) { - // do nothing - } - - if (providedHome == null) { - if (dspaceHome != null && !dspaceHome.equals("") && - !dspaceHome.equals("${" + DSpaceConfigurationService.DSPACE_HOME + "}")) { - File test = new File(dspaceHome); - if (test.exists() && new File(test, DSpaceConfigurationService.DSPACE_CONFIG_PATH).exists()) { - providedHome = dspaceHome; - } - } - return providedHome; - } - return providedHome; - } - + /** Utility class that will destroy the DSpace Kernel on Spring Boot shutdown */ private class DSpaceKernelDestroyer implements ApplicationListener { private DSpaceKernelImpl kernelImpl; @@ -229,4 +196,67 @@ public class Application extends SpringBootServletInitializer { } } } + + /** Utility class that will initialize the DSpace Kernel on Spring Boot startup */ + private class DSpaceKernelInitializer implements ApplicationContextInitializer { + + 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". + * If not found, look for "dspace.dir" initial context parameter. + */ + private String getProvidedHome(String dspaceHome) { + String providedHome = null; + try { + Context ctx = new InitialContext(); + providedHome = (String) ctx.lookup("java:/comp/env/" + DSpaceConfigurationService.DSPACE_HOME); + } catch (Exception e) { + // do nothing + } + + if (providedHome == null) { + if (dspaceHome != null && !dspaceHome.equals("") && + !dspaceHome.equals("${" + DSpaceConfigurationService.DSPACE_HOME + "}")) { + File test = new File(dspaceHome); + if (test.exists() && new File(test, DSpaceConfigurationService.DSPACE_CONFIG_PATH).exists()) { + providedHome = dspaceHome; + } + } + } + return providedHome; + } + } } \ No newline at end of file diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/RestResourceController.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/RestResourceController.java index ba5718ff13..e5b480a2fb 100644 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/RestResourceController.java +++ b/dspace-spring-rest/src/main/java/org/dspace/app/rest/RestResourceController.java @@ -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 links = new ArrayList(); @@ -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> resources; try { resources = repository.findAll(page).map(repository::wrapResource); + resources.forEach(linkService::addLinks); } catch (PaginationException pe) { resources = new PageImpl>(new ArrayList>(), page, pe.getTotal()); } @@ -320,6 +328,7 @@ public class RestResourceController implements InitializingBean { ResourceSupport result = null; if (returnPage) { Page> resources = ((Page) searchResult).map(repository::wrapResource); + resources.forEach(linkService::addLinks); result = assembler.toResource(resources, link); } else { result = repository.wrapResource((T) searchResult); diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/DSpaceResourceHalLinkFactory.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/DSpaceResourceHalLinkFactory.java index a5007fbaf9..5811f02e61 100644 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/DSpaceResourceHalLinkFactory.java +++ b/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/DSpaceResourceHalLinkFactory.java @@ -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 { +public class DSpaceResourceHalLinkFactory extends HalLinkFactory { + @Autowired + private Utils utils; protected void addLinks(DSpaceResource halResource, LinkedList 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(); + } + + 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 getControllerClass() { - return null; + protected Class getControllerClass() { + return RestResourceController.class; } protected Class getResourceClass() { - return null; + return DSpaceResource.class; } protected String getSelfLink(DSpaceResource halResource) { - return null; + return utils.linkToSingleResource(halResource.getData(), Link.REL_SELF).getHref(); } } diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/HalLinkFactory.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/HalLinkFactory.java index 684daa9b4f..5278c8a4b3 100644 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/HalLinkFactory.java +++ b/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/HalLinkFactory.java @@ -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 { public boolean supports(Class clazz) { - if(Objects.equals(clazz, getResourceClass())){ + if(getResourceClass().isAssignableFrom(clazz)){ return true; } return false; diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/ItemResourceHalLinkFactory.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/ItemResourceHalLinkFactory.java deleted file mode 100644 index 95324dd59b..0000000000 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/ItemResourceHalLinkFactory.java +++ /dev/null @@ -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 { - - protected void addLinks(ItemResource halResource, LinkedList list) { - - } - - protected Class getControllerClass() { - return null; - } - - protected Class getResourceClass() { - return null; - } - - protected String getSelfLink(ItemResource halResource) { - return null; - } - -} diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/search/SearchSupportHalLinkFactory.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/search/SearchSupportHalLinkFactory.java index a1ec4554ee..3dc1e55083 100644 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/search/SearchSupportHalLinkFactory.java +++ b/dspace-spring-rest/src/main/java/org/dspace/app/rest/link/search/SearchSupportHalLinkFactory.java @@ -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 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))); } diff --git a/dspace-spring-rest/src/main/java/org/dspace/app/rest/model/hateoas/DSpaceResource.java b/dspace-spring-rest/src/main/java/org/dspace/app/rest/model/hateoas/DSpaceResource.java index 1a2a8330f9..01293df9bf 100644 --- a/dspace-spring-rest/src/main/java/org/dspace/app/rest/model/hateoas/DSpaceResource.java +++ b/dspace-spring-rest/src/main/java/org/dspace/app/rest/model/hateoas/DSpaceResource.java @@ -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 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 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 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 getEmbedded() { return embedded; }