Tuesday, January 6, 2015

Custom Property Placeholder With Spring 4 and AWS S3 or RDBMS or NoSQL Database

Traditionally we use property files to load properties like 

<context:property-placeholder location="/WEB-INF/database.properties" ignore-unresolvable="true" ignore-resource-not-found="true"/>
So if you place the property file inside the server you need to re- deploy every time we did a change in the property values
The work around is place a property file in a remote location like Amazon S3 or a database

import java.io.IOException;
import java.io.StringWriter;
import java.util.Properties;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;

public class CustomPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

@Override
    protected void loadProperties(final Properties props) throws IOException {
props.putAll(getProperties("database.properties"));
}

private Properties getProperties(String key) throws IOException{
String accessKey = "XXXXXX";
String secretKey = "YYYYYYYYYYY";
String bucketName = "ZZZZZZZZZ";
 
AWSCredentials awsCredentials =  new BasicAWSCredentials(accessKey,secretKey);
AmazonS3  client=new AmazonS3Client(awsCredentials);
S3Object object = client.getObject(bucketName, key);  
S3ObjectInputStream objectContent = object.getObjectContent();
StringWriter writer=new StringWriter();
IOUtils.copy(objectContent, writer);  
return PropertiesConverter.stringToProperties(writer.toString());
}
}



import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;

import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;
import org.springframework.util.StringUtils;


public final class PropertiesConverter {
private static final PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
// prevents the class from being instantiated
private PropertiesConverter() {
};

public static Properties stringToProperties(String stringToParse) {
if (stringToParse == null) {
return new Properties();
}
if (!contains(stringToParse, "\n")) {
return StringUtils.splitArrayElementsIntoProperties(StringUtils
.commaDelimitedListToStringArray(stringToParse), "=");
}
StringReader stringReader = new StringReader(stringToParse);
Properties properties = new Properties();

try {
propertiesPersister.load(properties, stringReader);
}
catch (IOException ex) {
throw new IllegalStateException("Error while trying to parse String to java.util.Properties,"
+ " given String: " + properties);
}
return properties;
}

private static boolean contains(String str, String searchStr) {
return str.indexOf(searchStr) != -1;
}
}


You can invoke from spring config like

<bean id="placeholderPropertiesDatabase" class="xxx.xxxxxxxxxx.CustomPropertyPlaceholderConfigurer" >
<property name="order" value="1" />

    </bean>

Also you can edit the CustomPropertyPlaceholderConfigurer.java loadProperties method to load properties from RDBMS or NoSQL Database