1

I have a class called ConfigManagement which uses only static methods/fields. One of the static methods, called initializeConfig() takes a Property object (points at application.properties) as input and populates the fields and calls some other methods with the values from the application.properties file.

public class ConfigManagement {

   private static String signatureAlgorithm;
   private static String myName;
   private static RSAPublicKey myPublicKey;
   private static RSAPrivateKey myPrivateKey;
   private static HashMap<String, RSAPublicKey> peerPubKeys = new HashMap<String, RSAPublicKey>();
   private static boolean isInitialized = false;
/**
 * @return the signatureAlgorithm
 */

public static void initializeConfig(Properties props)  {
    signatureAlgorithm = props.getProperty("cybertrust.crypto.signatureAlgorithm");
    myName = props.getProperty("cybertrust.crypto.myName");
    try {
        try {
            myPublicKey = Loader.getPublicKeyFromCertificateFile(props.getProperty("cybertrust.crypto.myCertificate"));
        }
        catch (Exception e) {
            throw new IllegalStateException("cybertrust.crypto.myCertificate is empty, the file is not found or it contains invalid data");
        }
        try {
            myPrivateKey = Loader.getPrivateKeyFromFile(props.getProperty("cybertrust.crypto.myPrivateKey"));
        }
        catch (Exception e) {
            throw new IllegalStateException("cybertrust.crypto.myPrivateKey is empty, the file is not found or it contains invalid data");
        }
        peerPubKeys.put(myName, myPublicKey);
        int peerCounter = 0;
        do {
            String peerNameProp = String.format("cybertrust.crypto.peerModules.%d.name", peerCounter);
            String peerName = props.getProperty(peerNameProp);
            if (peerName == null)
                break;
            String peerNameCertFileProp = String.format("cybertrust.crypto.peerModules.%d.certificate", peerCounter);
            String peerNameCertFile = props.getProperty(peerNameCertFileProp);
            if (peerNameCertFile == null) // Do not halt the program, produce though an error
                Logger.getLogger("ConfigManagement").log(Level.SEVERE, 
                        String.format("Property %s not found while property %s is defined", peerNameCertFile, peerNameProp));
                // instantiate public key from file
                try {
                    RSAPublicKey peerRsaPubKey = Loader.getPublicKeyFromCertificateFile(peerNameCertFile); 
                    peerPubKeys.put(peerName, peerRsaPubKey);
                }
                catch (Exception e) {
                    Logger.getLogger("ConfigManagement").log(Level.SEVERE, 
                            String.format("File %s specified in property %s not found or does not contains a valid RSA key", peerNameCertFile, peerNameCertFileProp));              }
                peerCounter++;
        } while (true);
    }
    catch (Exception e) {
        throw(e);
    }
    if ((myPublicKey == null) || (signatureAlgorithm == null) || (myName == null))
        throw new IllegalStateException("one of the properties cybertrust.crypto.signatureAlgorithm, cybertrust.crypto.myName, cybertrust.crypto.myPublicKey, cybertrust.crypto.myPrivateKey is not defined");  
    isInitialized = true;
}

private static void testInitialized() {
    if (!isInitialized)
        throw new IllegalStateException("The configuration has not been initialized");
}

public static String getSignatureAlgorithm() {
    testInitialized();
    return signatureAlgorithm;
}
/**
 * @return the myName
 */
public static String getMyName() {
    testInitialized();
    return myName;
}
/**
 * @return the myPublicKey
 */
public static RSAPublicKey getMyPublicKey() {
    testInitialized();
    return myPublicKey;
}
/**
 * @return the myPrivateKey
 */
public static RSAPrivateKey getMyPrivateKey() {
    testInitialized();
    return myPrivateKey;
}

public static RSAPublicKey getPublicKey(String peerName) throws NoSuchElementException {
    testInitialized();
    RSAPublicKey result = peerPubKeys.get(peerName);
    if (result == null)
        throw new NoSuchElementException("No known key for module " + peerName);
    else
        return result;
}
}

The application.properties file looks something like this:

cybertrust.crypto.myName=tms1235.cybertrust.eu
cybertrust.crypto.myCertificate=tms1235.cert.pem
cybertrust.crypto.myPrivateKey=tms1235.key.pem

cybertrust.crypto.signatureAlgorithm=SHA256withRSA

cybertrust.crypto.peerModules.0.name=sga1234.cybertrust.eu
cybertrust.crypto.peerModules.0.certificate=sga1234.cert.pem

cybertrust.crypto.peerModules.1.name=tms1234.cybertrust.eu
cybertrust.crypto.peerModules.1.certificate=tms1234.cert.pem

In a simple Java project I run ConfigManagement.initializeConfig(props); in main() and the fields are initialized and I can use the rest of the methods. In Spring it's not that simple. I am trying to integrate this code in a SpringBoot application and I don't know how/where to initialize this class. I am posting the Spring configuration for reference:

@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.cybertrust.tms")
//@PropertySource({ "classpath:persistence-mysql.properties" })
@PropertySource({ "classpath:model.properties" })
public class DemoAppConfig implements WebMvcConfigurer {

    @Autowired
    private Environment env;

    private Logger logger = Logger.getLogger(getClass().getName());

    // define a bean for ViewResolver

    @Bean
    public DataSource myDataSource() {

        // create connection pool
        ComboPooledDataSource myDataSource = new ComboPooledDataSource();

        // set the jdbc driver
        try {
            myDataSource.setDriverClass("com.mysql.cj.jdbc.Driver");        
        }
        catch (PropertyVetoException exc) {
            throw new RuntimeException(exc);
        }

        // for sanity's sake, let's log url and user ... just to make sure we are reading the data
        logger.info("jdbc.url=" + env.getProperty("spring.datasource.url"));
        logger.info("jdbc.user=" + env.getProperty("spring.datasource.username"));

        // set database connection props
        myDataSource.setJdbcUrl(env.getProperty("spring.datasource.url"));
        myDataSource.setUser(env.getProperty("spring.datasource.username"));
        myDataSource.setPassword(env.getProperty("spring.datasource.password"));

        // set connection pool props
        myDataSource.setInitialPoolSize(getIntProperty("connection.pool.initialPoolSize"));
        myDataSource.setMinPoolSize(getIntProperty("connection.pool.minPoolSize"));
        myDataSource.setMaxPoolSize(getIntProperty("connection.pool.maxPoolSize"));     
        myDataSource.setMaxIdleTime(getIntProperty("connection.pool.maxIdleTime"));

        return myDataSource;
    }

    private Properties getHibernateProperties() {

        // set hibernate properties
        Properties props = new Properties();

        props.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
        props.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
        props.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));

        return props;               
    }


    // need a helper method 
    // read environment property and convert to int

    private int getIntProperty(String propName) {

        String propVal = env.getProperty(propName);

        // now convert to int
        int intPropVal = Integer.parseInt(propVal);

        return intPropVal;
    }   

    @Bean
    public LocalSessionFactoryBean sessionFactory(){

        // create session factorys
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();

        // set the properties
        sessionFactory.setDataSource(myDataSource());
        sessionFactory.setPackagesToScan(env.getProperty("hibernate.packagesToScan"));
        sessionFactory.setHibernateProperties(getHibernateProperties());

        return sessionFactory;
    }

    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {

        // setup transaction manager based on session factory
        HibernateTransactionManager txManager = new HibernateTransactionManager();
        txManager.setSessionFactory(sessionFactory);

        return txManager;
    }


    @Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }

    @Bean
    public ConfigManagement configManagement() {
        return new ConfigManagement();
    }


}

And the Spring Boot main():

@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.cybertrust.tms")
//@PropertySource({ "classpath:persistence-mysql.properties" })
@PropertySource({ "classpath:model.properties" })
//@EnableAutoConfiguration(exclude = HibernateJpaAutoConfiguration.class)

@SpringBootApplication(exclude = {HibernateJpaAutoConfiguration.class})
public class TMS extends SpringBootServletInitializer {

    public static void main(String[] args) throws Exception {

        SpringApplication.run(TMS.class, args);
    }

}
2
  • Why you are using static method instead of injecting a property with @Value Commented Apr 2, 2020 at 11:48
  • @user7294900 Because this code was originally written for simple Java application and now I have to integrate it to this Spring application. I will try to rewrite the code to use @Value as you proposed and see what happens. Commented Apr 2, 2020 at 12:14

2 Answers 2

1

Your static solution won't work in a Spring environment as is, because static can be executed before Spring is up and loaded all beans and properties

You should rewrite your code in a Spring way by getting propertyies using @Value

Injecting a property with the @Value annotation is straightforward:

@Value( "${jdbc.url}" ) private String jdbcUrl;

Sign up to request clarification or add additional context in comments.

2 Comments

In the ConfigManagement class, I have some variables that are not primitive types, like RSAPublicKey and HashMap<String, RSAPublicKey>. These cannot be initialized using @Value and are initialized through method calls with the properties as parameters. How should I initialize these?
@GiannisPappas I think this is a separate question, but you can add load logic in spring component
1

In order to integrate this code to a Spring project, I had to:

Make the class a bean, managed by Spring, by adding it in my configuration file that I posted in my question, I added this:

@Bean
public ConfigManagement configManagement() {
    return new ConfigManagement();
}

Remove the static declaration from the class properties and use the @Value annotation to initialize them from the application.properties file, as suggested by @user7294900.

However, some of the class properties were not primitive types and couldn't be initialized directly from the application.properties. They needed some "business-logic" to be run at initialization time. In order to achieve that, I had to remove the static declaration and add the @PostConstruct annotation in the initializeConfig() method, which is the one that handled the initialization of the rest of the properties.

public class ConfigManagement {

    @Value("${cybertrust.crypto.signatureAlgorithm}")
    private String signatureAlgorithm;
    @Value("${cybertrust.crypto.myName}")
    private String myName;
    @Value("${cybertrust.crypto.myCertificate}")
    private String myCertificate;
    @Value("${cybertrust.crypto.myPrivateKey}")
    private String myPrivateKey;
    private RSAPublicKey myRSAPublicKey;
    private RSAPrivateKey myRSAPrivateKey;
    private HashMap<String, RSAPublicKey> peerPubKeys = new HashMap<String, RSAPublicKey>();
    private boolean isInitialized = false;
    int peerCounter;
    /**
     * @return the signatureAlgorithm
     */

    public ConfigManagement() {

    }

    @PostConstruct
    public void initializeConfig() throws Exception  {

        try {
            try {
                myRSAPublicKey = Loader.getPublicKeyFromCertificateFile("C:\\Users\\Findorgri\\git\\trust-management\\TMS-rest\\" + myCertificate);
            }
            catch (Exception e) {
                throw new IllegalStateException("cybertrust.crypto.myCertificate is empty, the file is not found or it contains invalid data");
            }
            try {
                myRSAPrivateKey = Loader.getPrivateKeyFromFile("C:\\Users\\Findorgri\\git\\trust-management\\TMS-rest\\" + myPrivateKey);
            }
            catch (Exception e) {
                throw new IllegalStateException("cybertrust.crypto.myPrivateKey is empty, the file is not found or it contains invalid data");
            }
            peerPubKeys.put(myName, myRSAPublicKey);

            Properties props = loadProperties("C:\\Users\\Findorgri\\git\\trust-management\\TMS-rest\\src\\main\\resources\\application.properties");
            if (props == null) {
                throw new Exception("Properties file not found");
            }

            peerCounter = 0;
            do {
                String peerNameProp = String.format("cybertrust.crypto.peerModules.%d.name", peerCounter);

                String peerName = props.getProperty(peerNameProp);
                System.out.println("####TEST####\n" + peerNameProp + "\n" + peerName +"\n####TEST####");
                if (peerName == null)
                    break;
                String peerNameCertFileProp = String.format("cybertrust.crypto.peerModules.%d.certificate", peerCounter);
                String peerNameCertFile = props.getProperty(peerNameCertFileProp);
                System.out.println("####TEST####\n" + peerNameCertFileProp + "\n" + peerNameCertFile +"\n####TEST####");

                if (peerNameCertFile == null) // Do not halt the program, produce though an error
                    Logger.getLogger("ConfigManagement").log(Level.SEVERE, 
                            String.format("Property %s not found while property %s is defined", peerNameCertFile, peerNameProp));
                // instantiate public key from file
                try {
                    RSAPublicKey peerRsaPubKey = Loader.getPublicKeyFromCertificateFile("C:\\Users\\Findorgri\\git\\trust-management\\TMS-rest\\" + peerNameCertFile); 
                    peerPubKeys.put(peerName, peerRsaPubKey);
                }
                catch (Exception e) {
                    Logger.getLogger("ConfigManagement").log(Level.SEVERE, 
                            String.format("File %s specified in property %s not found or does not contains a valid RSA key", peerNameCertFile, peerNameCertFileProp));              }
                peerCounter++;
            } while (true);
        }
        catch (Exception e) {
            throw(e);
        }
        if ((myRSAPublicKey == null) || (signatureAlgorithm == null) || (myName == null))
            throw new IllegalStateException("one of the properties cybertrust.crypto.signatureAlgorithm, cybertrust.crypto.myName, cybertrust.crypto.myPublicKey, cybertrust.crypto.myPrivateKey is not defined");
        isInitialized = true;
        peerPubKeys.forEach((key, value) -> System.out.println(key + ":" + value));

    }
....

Finally, for completeness' sake, for the initializeConfig() method to have access at the application.properties I had to use this method:

private static Properties loadProperties(String fileName) throws IOException {
        FileInputStream fis = null;
        Properties prop = null;
        try {
            fis = new FileInputStream(fileName);
            prop = new Properties();
            prop.load(fis);
        } catch(FileNotFoundException fnfe) {
            fnfe.printStackTrace();
        } catch(IOException ioe) {
            ioe.printStackTrace();
        } finally {
            fis.close();
        }
        return prop;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.