Refactor Application startup to load DSpace configs early in boot process. Also fixes DS-3492

This commit is contained in:
Tim Donohue
2018-11-28 17:11:08 +00:00
parent 7f3877c7bc
commit fa61b737db
6 changed files with 98 additions and 65 deletions

View File

@@ -14,6 +14,7 @@ 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.rest.utils.DSpaceConfigurationInitializer;
import org.dspace.app.rest.utils.DSpaceKernelInitializer;
import org.dspace.app.util.DSpaceContextListener;
import org.dspace.utils.servlet.DSpaceWebappServletFilter;
@@ -22,7 +23,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
@@ -62,7 +62,6 @@ public class Application extends SpringBootServletInitializer {
* This is necessary to allow us to build a deployable WAR, rather than
* always relying on embedded Tomcat.
* <p>
* <p>
* See: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file
*
* @param application
@@ -70,13 +69,10 @@ public class Application extends SpringBootServletInitializer {
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// Pass this Application class, and our initializers for DSpace Kernel and Configuration
// NOTE: Kernel must be initialized before Configuration
return application.sources(Application.class)
.initializers(new DSpaceKernelInitializer());
}
@Bean
public ServletContextInitializer contextInitializer() {
return servletContext -> servletContext.setInitParameter("dspace.dir", configuration.getDspaceHome());
.initializers(new DSpaceKernelInitializer(), new DSpaceConfigurationInitializer());
}
/**
@@ -89,7 +85,6 @@ public class Application extends SpringBootServletInitializer {
@Order(2)
protected DSpaceContextListener dspaceContextListener() {
// This listener initializes the DSpace Context object
// (and loads all DSpace configs)
return new DSpaceContextListener();
}

View File

@@ -13,27 +13,24 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
/**
* This class provide extra configuration for our Spring Boot Application
* This class provides extra configuration for our Spring Boot Application
* <p>
* NOTE: @ComponentScan on "org.dspace.app.configuration" provides a way for other modules or plugins
* to "inject" their own configurations / subpaths into our Spring Boot webapp.
* NOTE: @ComponentScan on "org.dspace.app.configuration" provides a way for other DSpace modules or plugins
* to "inject" their own Spring configurations / subpaths into our Spring Boot webapp.
*
* @author Andrea Bollini (andrea.bollini at 4science.it)
* @author Tim Donohue
*/
@Configuration
@EnableSpringDataWebSupport
@ComponentScan( {"org.dspace.app.rest.converter", "org.dspace.app.rest.repository", "org.dspace.app.rest.utils",
"org.dspace.app.configuration"})
public class ApplicationConfig {
@Value("${dspace.dir}")
private String dspaceHome;
@Value("${cors.allowed-origins}")
// Allowed CORS origins. Defaults to * (everywhere)
// Can be overridden in DSpace configuration
@Value("${rest.cors.allowed-origins:*}")
private String corsAllowedOrigins;
public String getDspaceHome() {
return dspaceHome;
}
public String[] getCorsAllowedOrigins() {
if (corsAllowedOrigins != null) {
return corsAllowedOrigins.split("\\s*,\\s*");

View File

@@ -0,0 +1,48 @@
/**
* 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.utils;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.spring.ConfigurationPropertySource;
import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Utility class that will initialize the DSpace Configuration on Spring Boot startup.
* <P>
* NOTE: MUST be loaded after DSpaceKernelInitializer, as it requires the kernel is already initialized.
* <P>
* This initializer ensures that our DSpace Configuration is loaded into Spring's list of PropertySources
* very early in the Spring Boot startup process. That is important as it allows us to use DSpace configurations
* within @ConditionalOnProperty annotations on beans, as well as @Value annotations and XML bean definitions.
* <P>
* Used by org.dspace.app.rest.Application
*/
public class DSpaceConfigurationInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger log = LoggerFactory.getLogger(DSpaceConfigurationInitializer.class);
@Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
// Load DSpace Configuration service (requires kernel already initialized)
ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
Configuration configuration = configurationService.getConfiguration();
// Create an Apache Commons Configuration Property Source from our configuration
ConfigurationPropertySource apacheCommonsConfigPropertySource =
new ConfigurationPropertySource(configuration.getClass().getName(), configuration);
// Append it to the Environment's list of PropertySources
applicationContext.getEnvironment().getPropertySources().addLast(apacheCommonsConfigPropertySource);
}
}

View File

@@ -11,6 +11,7 @@ import java.io.File;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.apache.commons.lang3.StringUtils;
import org.dspace.kernel.DSpaceKernel;
import org.dspace.kernel.DSpaceKernelManager;
import org.dspace.servicemanager.DSpaceKernelImpl;
@@ -22,6 +23,7 @@ import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* Utility class that will initialize the DSpace Kernel on Spring Boot startup.
@@ -35,16 +37,16 @@ public class DSpaceKernelInitializer implements ApplicationContextInitializer<Co
@Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
String dspaceHome = applicationContext.getEnvironment().getProperty("dspace.dir");
// Check if the kernel is already started
this.dspaceKernel = DSpaceKernelManager.getDefaultKernel();
if (this.dspaceKernel == null) {
DSpaceKernelImpl kernelImpl = null;
try {
// Load the kernel with default settings
kernelImpl = DSpaceKernelInit.getKernel(null);
if (!kernelImpl.isRunning()) {
kernelImpl.start(getProvidedHome(dspaceHome)); // init the kernel
// Determine configured DSpace home & init the Kernel
kernelImpl.start(getDSpaceHome(applicationContext.getEnvironment()));
}
this.dspaceKernel = kernelImpl;
@@ -65,8 +67,8 @@ public class DSpaceKernelInitializer implements ApplicationContextInitializer<Co
}
if (applicationContext.getParent() == null) {
//Set the DSpace Kernel Application context as a parent of the Spring Boot context so that
//we can auto-wire all DSpace Kernel services
// 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(dspaceKernel.getServiceManager().getApplicationContext());
//Add a listener for Spring Boot application shutdown so that we can nicely cleanup the DSpace kernel.
@@ -75,29 +77,35 @@ public class DSpaceKernelInitializer implements ApplicationContextInitializer<Co
}
/**
* Find DSpace's "home" directory.
* Find DSpace's "home" directory (from current environment)
* Initially look for JNDI Resource called "java:/comp/env/dspace.dir".
* If not found, look for "dspace.dir" initial context parameter.
* If not found, use value provided in "dspace.dir" in Spring Environment
*/
private String getProvidedHome(String dspaceHome) {
String providedHome = null;
private String getDSpaceHome(ConfigurableEnvironment environment) {
// Load the "dspace.dir" property from Spring Boot's Configuration (application.properties)
// This gives us the location of our DSpace configurations, necessary to start the kernel
String providedHome = environment.getProperty(DSpaceConfigurationService.DSPACE_HOME);
String dspaceHome = null;
try {
// Allow ability to override home directory via JNDI
Context ctx = new InitialContext();
providedHome = (String) ctx.lookup("java:/comp/env/" + DSpaceConfigurationService.DSPACE_HOME);
dspaceHome = (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);
// Otherwise, verify the 'providedHome' value is non-empty, exists and includes DSpace configs
if (dspaceHome == null) {
if (StringUtils.isNotBlank(providedHome) &&
!providedHome.equals("${" + DSpaceConfigurationService.DSPACE_HOME + "}")) {
File test = new File(providedHome);
if (test.exists() && new File(test, DSpaceConfigurationService.DSPACE_CONFIG_PATH).exists()) {
providedHome = dspaceHome;
dspaceHome = providedHome;
}
}
}
return providedHome;
return dspaceHome;
}

View File

@@ -13,28 +13,14 @@
# For common settings see:
# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
#
#
# TODO: Eventually would could think of "wiring" this up to use Commons Configuration as well
# See, for example: http://stackoverflow.com/questions/25271537/remote-propertysource
# and https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
########################
# DSpace Settings
#
# DSpace installation directory
# DSpace home/installation directory
# REQUIRED to be specified in this application.properties file, as it is used to locate and initialize
# the DSpace Kernel and all Services (including configuration). See org.dspace.app.rest.Application.getDSpaceHome()
dspace.dir=${dspace.dir}
#dspace.dir=d:/install/dspace7
########################
# DSpace API CORS Settings
#
cors.allowed-origins = *
########################
# Spring Boot Settings
#
# Testing an "application Name"
spring.application.name = DSpace Spring Rest
########################
# Spring DATA Rest settings
@@ -75,15 +61,6 @@ server.port=8080
# (Optional, defaults to root context)
#server.context-path=/spring-data-rest
# This creates a Tomcat context-param named "dspace.dir"
# and sets it to the value of the "dspace.dir" property (listed above)
server.context-parameters.dspace.dir=${dspace.dir}
# This creates a Tomcat context-param named "dspace-config"
# (Used by DSpaceContextListener to load the configurations)
# This is only needed in DSpace 5 or below to initialize ConfigurationManager
#server.context-parameters.dspace-config=${dspace.dir}/config/dspace.cfg
# Error handling settings
# Always include the fullstacktrace in error pages
# Can be set to "never" if you don't want it.

View File

@@ -1,9 +1,17 @@
#---------------------------------------------------------------#
#--------------------REST CONFIGURATIONS------------------------#
#---------------------------------------------------------------#
# These configs are used by the REST module #
# These configs are used by the RESTv7 module #
#---------------------------------------------------------------#
# Allowed CORS origins. Defaults to * (everywhere)
# Multiple allowed origin URLs may be comma separated
# (Requires reboot of servlet container, e.g. Tomcat, to reload)
rest.cors.allowed-origins = *
#---------------------------------------------------------------#
# These configs are used by the deprecated REST (v4-6) module #
#---------------------------------------------------------------#
# record stats in DSpace statistics module
rest.stats = true
@@ -137,4 +145,4 @@ rest.report-regex-xml-entity = ^.*&#.*$
rest.report-regex-non-ascii = ^.*[^\\p{ASCII}].*$
# The maximum number of results to return for 1 request
rest.search.max.results = 100
rest.search.max.results = 100