Giannis Pappas
Giannis Pappas

Reputation: 137

Initialize class with static method not managed by Spring

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);
    }

}

Upvotes: 1

Views: 2251

Answers (2)

Giannis Pappas
Giannis Pappas

Reputation: 137

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;
    }

Upvotes: 1

Ori Marko
Ori Marko

Reputation: 58782

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;

Upvotes: 1

Related Questions