diff --git a/bookstore/src/main/java/com/fjordtek/bookstore/config/AdditionalPropertiesConfig.java b/bookstore/src/main/java/com/fjordtek/bookstore/config/AdditionalPropertiesConfig.java index c7ede3d..9e780e3 100644 --- a/bookstore/src/main/java/com/fjordtek/bookstore/config/AdditionalPropertiesConfig.java +++ b/bookstore/src/main/java/com/fjordtek/bookstore/config/AdditionalPropertiesConfig.java @@ -4,6 +4,7 @@ package com.fjordtek.bookstore.config; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.springframework.boot.SpringApplication; @@ -24,6 +25,9 @@ import org.springframework.core.io.support.PropertiesLoaderUtils; * Main purpose of this implementation is to load additional properties files into Spring * environment as early as possible during Spring Boot initialization process. *

+* Additionally, this class provides processing methods for Spring profile +* properties configuration. +*

* Loading of this class must be enabled in /resources/META-INF/spring.factories file. * * @author Pekka Helenius @@ -33,15 +37,120 @@ import org.springframework.core.io.support.PropertiesLoaderUtils; //@Order(Ordered.LOWEST_PRECEDENCE) public class AdditionalPropertiesConfig implements EnvironmentPostProcessor { + // Add global properties private List getAdditionalResources() { - List resources = new ArrayList(); + List classPathResources = new ArrayList(); - // Add your additional (classpath, filesystem, etc.) properties files here - resources.add(new ClassPathResource("website.properties")); - resources.add(new ClassPathResource("authentication.properties")); - resources.add(new ClassPathResource("categories.properties")); + /* + * Add your additional global properties files here + */ + classPathResources.add(new ClassPathResource("website.properties")); + classPathResources.add(new ClassPathResource("authentication.properties")); + classPathResources.add(new ClassPathResource("categories.properties")); + + return classPathResources; + } + + // Add predefined profile properties + private List getAdditionalProfileResources(String[] profiles) { + List classPathResources = new ArrayList(); + + /* + * Add your additional profile property file prefixes here + * For instance, if profile is 'dev': + * "database" ->> "database-dev.properties" + */ + String[] resourceFilePrefixes = { + "database" + }; + + for (String profile : profiles) { + + for (String prefix : resourceFilePrefixes) { + try { + ClassPathResource classPathResource = new ClassPathResource(prefix + "-" + profile + ".properties"); + classPathResource.getFile().canRead(); + classPathResources.add(classPathResource); + System.out.printf( + "Profile properties file found: %s\n", + classPathResource.getFilename() + ); + } catch (IOException e) { + + if ( prefix.equals("database") && profile.equals("prod") ) { + + System.err.println(String.join("\n", + "Production mode was enabled. However the following issue occured.", + "", + "Missing file: database-prod.properties", + "", + "File is not found from application class path.", + "", + "Please provide the missing file with following entries:", + "", + "spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver", + "spring.datasource.url = jdbc:mysql://:/ ...", + "spring.datasource.username = ", + "spring.datasource.password = ", + "spring.datasource.initialization-mode = always", + "", + "spring.jpa.hibernate.use-new-id-generator-mappings = false", + "spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl", + "", + "Unable to run application. Exiting." + ) + ); + System.exit(1); + + } else { + System.err.println( + "Resource " + + prefix + "-" + profile + ".properties" + + " not found!" + ); + } + } + continue; + } + } + return classPathResources; + } + + private void checkPropertiesExist( + ConfigurableEnvironment environment, + String[] propEntries + ) { + + List missingProperties = new ArrayList(); + + for (String prop : propEntries) { + try { + environment.getRequiredProperty(prop); + } catch (IllegalStateException e) { + missingProperties.add(prop); + } + } + + if (missingProperties.size() != 0) { + System.err.printf( + "\nFollowing, required application property entries are missing:\n\n" + ); + int i = 0; + while (i < missingProperties.size()) { + System.err.printf( + "%s\n", + missingProperties.get(i) + ); + i++; + } + System.err.printf( + "\nCannot continue. Exiting. (Active profiles: %s)\n", + Arrays.stream(environment.getActiveProfiles()) + .toArray() + ); + System.exit(1); + } - return resources; } private void showSpringConfiguration(MutablePropertySources properties) { @@ -60,19 +169,41 @@ public class AdditionalPropertiesConfig implements EnvironmentPostProcessor { MutablePropertySources propertySources = environment.getPropertySources(); - for (Resource res : getAdditionalResources()) { - try { - propertySources.addLast( - new PropertiesPropertySource( - res.getFilename(), - PropertiesLoaderUtils.loadProperties(res) - ) - ); - } catch (IOException e) { - e.printStackTrace(); + List> additionalResources = new ArrayList>(); + + additionalResources.add(getAdditionalResources()); + additionalResources.add(getAdditionalProfileResources(environment.getActiveProfiles())); + + for (List resList : additionalResources) { + for (Resource res : resList) { + try { + propertySources.addLast( + new PropertiesPropertySource( + res.getFilename(), + PropertiesLoaderUtils.loadProperties(res) + ) + ); + } catch (IOException e) { + e.printStackTrace(); + } } } + if ( Arrays.stream(environment.getActiveProfiles()).anyMatch(p -> p.contains("prod")) ) { + checkPropertiesExist( + environment, + new String[] { + "spring.datasource.driver-class-name", + "spring.datasource.url", + "spring.datasource.username", + "spring.datasource.password", + "spring.datasource.initialization-mode", + "spring.jpa.hibernate.use-new-id-generator-mappings", + "spring.jpa.hibernate.naming.physical-strategy" + } + ); + } + showSpringConfiguration(propertySources); }