From 2a488ece6fb05a3fb0f5c02e9890a7fa345fc4b2 Mon Sep 17 00:00:00 2001 From: Rick Herrick <jrherrick@wustl.edu> Date: Mon, 12 Sep 2016 15:23:23 -0500 Subject: [PATCH] XNAT-4499 XNAT-4509 XNAT-4516 Added environment to XnatAppInfo to support flexible properties (specifically xnat.is-primary-node setting). Added property helper methods as well. Added StringHttpMessageConverter to allow returning strings as XML, JSON, etc. --- .../org/nrg/xnat/configuration/WebConfig.java | 2 + .../nrg/xnat/initialization/RootConfig.java | 9 +- .../tasks/MigrateDatabaseTables.java | 9 +- .../org/nrg/xnat/services/XnatAppInfo.java | 91 ++++++++++++++++--- 4 files changed, 92 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/nrg/xnat/configuration/WebConfig.java b/src/main/java/org/nrg/xnat/configuration/WebConfig.java index 0046e1c8..b560e3b5 100644 --- a/src/main/java/org/nrg/xnat/configuration/WebConfig.java +++ b/src/main/java/org/nrg/xnat/configuration/WebConfig.java @@ -14,6 +14,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; @@ -62,6 +63,7 @@ public class WebConfig extends WebMvcConfigurerAdapter { public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) { converters.add(new MappingJackson2HttpMessageConverter(_objectMapperBuilder.build())); converters.add(new MarshallingHttpMessageConverter(_marshaller, _marshaller)); + converters.add(new StringHttpMessageConverter()); } @Bean diff --git a/src/main/java/org/nrg/xnat/initialization/RootConfig.java b/src/main/java/org/nrg/xnat/initialization/RootConfig.java index 629dc908..d886b31e 100644 --- a/src/main/java/org/nrg/xnat/initialization/RootConfig.java +++ b/src/main/java/org/nrg/xnat/initialization/RootConfig.java @@ -20,15 +20,12 @@ import org.nrg.xnat.services.XnatAppInfo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.core.env.Environment; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.oxm.jaxb.Jaxb2Marshaller; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; import javax.servlet.ServletContext; -import javax.xml.bind.Marshaller; import java.io.BufferedReader; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -51,8 +48,8 @@ import java.util.Properties; @Import({PropertiesConfig.class, DatabaseConfig.class, SecurityConfig.class, ApplicationConfig.class}) public class RootConfig { @Bean - public XnatAppInfo appInfo(final SiteConfigPreferences preferences, final ServletContext context, final SerializerService serializerService, final JdbcTemplate template) throws IOException { - return new XnatAppInfo(preferences, context, serializerService, template); + public XnatAppInfo appInfo(final SiteConfigPreferences preferences, final ServletContext context, final Environment environment, final SerializerService serializerService, final JdbcTemplate template) throws IOException { + return new XnatAppInfo(preferences, context, environment, serializerService, template); } @Bean diff --git a/src/main/java/org/nrg/xnat/initialization/tasks/MigrateDatabaseTables.java b/src/main/java/org/nrg/xnat/initialization/tasks/MigrateDatabaseTables.java index c950f6ce..3433bad5 100644 --- a/src/main/java/org/nrg/xnat/initialization/tasks/MigrateDatabaseTables.java +++ b/src/main/java/org/nrg/xnat/initialization/tasks/MigrateDatabaseTables.java @@ -11,6 +11,7 @@ package org.nrg.xnat.initialization.tasks; import com.google.common.base.Joiner; import org.nrg.framework.orm.DatabaseHelper; import org.nrg.framework.utilities.BasicXnatResourceLocator; +import org.nrg.xnat.services.XnatAppInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -30,9 +31,10 @@ import java.util.Properties; @Component public class MigrateDatabaseTables extends AbstractInitializingTask { @Autowired - public MigrateDatabaseTables(final JdbcTemplate template, final TransactionTemplate transactionTemplate) { + public MigrateDatabaseTables(final JdbcTemplate template, final TransactionTemplate transactionTemplate, final XnatAppInfo appInfo) { super(); _db = new DatabaseHelper(template, transactionTemplate); + _appInfo = appInfo; } @Override @@ -86,6 +88,10 @@ public class MigrateDatabaseTables extends AbstractInitializingTask { } } } + if (_appInfo.isPrimaryNode()) { + _log.info("This service is the primary XNAT node, checking whether database updates are required."); + // Do the needful here. + } complete(); } catch (IOException e) { _log.error("An error occurred attempting to read table migration properties files", e); @@ -98,4 +104,5 @@ public class MigrateDatabaseTables extends AbstractInitializingTask { private static final String SQL_WARNING_TABLE = "The requested table"; private final DatabaseHelper _db; + private final XnatAppInfo _appInfo; } diff --git a/src/main/java/org/nrg/xnat/services/XnatAppInfo.java b/src/main/java/org/nrg/xnat/services/XnatAppInfo.java index 4559c9f8..4d3b80bb 100644 --- a/src/main/java/org/nrg/xnat/services/XnatAppInfo.java +++ b/src/main/java/org/nrg/xnat/services/XnatAppInfo.java @@ -7,6 +7,7 @@ import org.nrg.prefs.exceptions.InvalidPreferenceName; import org.nrg.xdat.preferences.SiteConfigPreferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -34,7 +35,8 @@ import java.util.regex.Pattern; @Component public class XnatAppInfo { - public static final String NON_RELEASE_VERSION_REGEX = "(?i:^.*(SNAPSHOT|BETA|RC).*$)"; + public static final String NON_RELEASE_VERSION_REGEX = "(?i:^.*(SNAPSHOT|BETA|RC).*$)"; + public static final String XNAT_PRIMARY_MODE_PROPERTY = "xnat.is_primary_node"; private static final int MILLISECONDS_IN_A_DAY = (24 * 60 * 60 * 1000); private static final int MILLISECONDS_IN_AN_HOUR = (60 * 60 * 1000); @@ -46,9 +48,11 @@ public class XnatAppInfo { private static final String SECONDS = "seconds"; @Inject - public XnatAppInfo(final SiteConfigPreferences preferences, final ServletContext context, final SerializerService serializerService, final JdbcTemplate template) throws IOException { - _preferences=preferences; + public XnatAppInfo(final SiteConfigPreferences preferences, final ServletContext context, final Environment environment, final SerializerService serializerService, final JdbcTemplate template) throws IOException { + _preferences = preferences; _template = template; + _environment = environment; + _primaryNode = Boolean.parseBoolean(_environment.getProperty(XNAT_PRIMARY_MODE_PROPERTY, "true")); final Resource configuredUrls = RESOURCE_LOADER.getResource("classpath:META-INF/xnat/security/configured-urls.yaml"); try (final InputStream inputStream = configuredUrls.getInputStream()) { @@ -164,11 +168,11 @@ public class XnatAppInfo { _log.info("The site was not flagged as initialized and initialized preference set to false. Setting system for initialization."); } for (String pref : _foundPreferences.keySet()) { - if(_foundPreferences.get(pref)!=null) { + if (_foundPreferences.get(pref) != null) { _template.update( "UPDATE xhbm_preference SET value = ? WHERE name = ?", new Object[]{_foundPreferences.get(pref), pref}, new int[]{Types.VARCHAR, Types.VARCHAR} - ); + ); try { _preferences.set(_foundPreferences.get(pref), pref); } catch (InvalidPreferenceName e) { @@ -176,9 +180,8 @@ public class XnatAppInfo { } catch (NullPointerException e) { _log.error("Error getting site config preferences.", e); } - } - else{ - _log.warn("Preference "+pref+" was null."); + } else { + _log.warn("Preference " + pref + " was null."); } } } @@ -210,6 +213,56 @@ public class XnatAppInfo { return (Properties) _properties.clone(); } + /** + * Gets the requested environment property. Returns null if the property doesn't exist in the environment. + * + * @param property The name of the property to retrieve. + * + * @return The value of the property if found, null otherwise. + */ + public String getConfiguredProperty(final String property) { + return getConfiguredProperty(property, (String) null); + } + + /** + * Gets the requested environment property. Returns the specified default value if the property doesn't exist in the + * environment. + * + * @param property The name of the property to retrieve. + * @param defaultValue The default value to return if the property isn't set in the environment. + * + * @return The value of the property if found, the specified default value otherwise. + */ + public String getConfiguredProperty(final String property, final String defaultValue) { + return _environment.getProperty(property, defaultValue); + } + + /** + * Gets the requested environment property. Returns null if the property doesn't exist in the environment. + * + * @param property The name of the property to retrieve. + * @param type The type of the property to retrieve. + * + * @return The value of the property if found, null otherwise. + */ + public <T> T getConfiguredProperty(final String property, final Class<T> type) { + return getConfiguredProperty(property, type, null); + } + + /** + * Gets the requested environment property. Returns the specified default value if the property doesn't exist in the + * environment. + * + * @param property The name of the property to retrieve. + * @param type The type of the property to retrieve. + * @param defaultValue The default value to return if the property isn't set in the environment. + * + * @return The value of the property if found, the specified default value otherwise. + */ + public <T> T getConfiguredProperty(final String property, final Class<T> type, final T defaultValue) { + return _environment.getProperty(property, type, defaultValue); + } + /** * Gets the version of the application. * @@ -297,6 +350,17 @@ public class XnatAppInfo { return uptime; } + /** + * Indicates whether this is a stand-alone XNAT server or the primary node in a distributed XNAT deployment, as + * opposed to a secondary node. The return value for this method is determined by the value set for the + * <b>xnat.is_primary_node</b> property. If no value is set for this property, it defaults to <b>true</b>. + * + * @return Returns true if this is a stand-alone XNAT server or the primary node in a distributed XNAT deployment. + */ + public boolean isPrimaryNode() { + return _primaryNode; + } + /** * Returns the system uptime in a formatted display string. * @@ -431,14 +495,17 @@ public class XnatAppInfo { private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader(); private final JdbcTemplate _template; + private final Environment _environment; - private final String _configPath; - private final Pattern _configPathPattern; + private final String _configPath; + private final Pattern _configPathPattern; private final AntPathRequestMatcher _configPathMatcher; - private final String _nonAdminErrorPath; - private final Pattern _nonAdminErrorPathPattern; + private final String _nonAdminErrorPath; + private final Pattern _nonAdminErrorPathPattern; private final AntPathRequestMatcher _nonAdminErrorPathMatcher; private final SiteConfigPreferences _preferences; + private final boolean _primaryNode; + private final Map<String, AntPathRequestMatcher> _openUrls = new HashMap<>(); private final Map<String, AntPathRequestMatcher> _adminUrls = new HashMap<>(); private final Map<String, AntPathRequestMatcher> _initPaths = new HashMap<>(); -- GitLab