Create a Pool of JAXB Unmarshaller - spring

I was looking around to find a way to improve JAXB Unmarshalling performances processing huge sets of files and found the following advice:
"If you really care about the performance, and/or your application is going to read a lot of small documents, then creating Unmarshaller could be relatively an expensive operation. In that case, consider pooling Unmarshaller objects"
Googling the web to find an example of this didn't return anything, so I thought it may be of interest to put my implementation here using Spring 3.0 and Apache Commons Pool.
UnmarshallerFactory.java
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import org.apache.commons.pool.KeyedPoolableObjectFactory;
import org.springframework.stereotype.Component;
/**
* Pool of JAXB Unmarshallers.
*
*/
#Component
public class UnmarshallerFactory implements KeyedPoolableObjectFactory {
// Map of JAXB Contexts
#SuppressWarnings("rawtypes")
private final static Map<Object, JAXBContext> JAXB_CONTEXT_MAP = new HashMap<Object, JAXBContext>();
#Override
public void activateObject(final Object arg0, final Object arg1) throws Exception {
}
#Override
public void passivateObject(final Object arg0, final Object arg1) throws Exception {
}
#Override
public final void destroyObject(final Object key, final Object object) throws Exception {
}
/**
* Create a new instance of Unmarshaller if none exists for the specified
* key.
*
* #param unmarshallerKey
* : Class used to create an instance of Unmarshaller
*/
#SuppressWarnings("rawtypes")
#Override
public final Object makeObject(final Object unmarshallerKey) {
if (unmarshallerKey instanceof Class) {
Class clazz = (Class) unmarshallerKey;
// Retrieve or create a JACBContext for this key
JAXBContext jc = JAXB_CONTEXT_MAP.get(unmarshallerKey);
if (jc == null) {
try {
jc = JAXBContext.newInstance(clazz);
// JAXB Context is threadsafe, it can be reused, so let's store it for later
JAXB_CONTEXT_MAP.put(unmarshallerKey, jc);
} catch (JAXBException e) {
// Deal with that error here
return null;
}
}
try {
return jc.createUnmarshaller();
} catch (JAXBException e) {
// Deal with that error here
}
}
return null;
}
#Override
public final boolean validateObject(final Object key, final Object object) {
return true;
}
}
UnmarshallerPool.java
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class UnmarshallerPool extends GenericKeyedObjectPool {
#Autowired
public UnmarshallerPool(final UnmarshallerFactory unmarshallerFactory) {
// Make usage of the factory created above
super(unmarshallerFactory);
// You'd better set the properties from a file here
this.setMaxIdle(4);
this.setMaxActive(5);
this.setMinEvictableIdleTimeMillis(30000);
this.setTestOnBorrow(false);
this.setMaxWait(1000);
}
public UnmarshallerPool(UnmarshallerFactory objFactory,
GenericKeyedObjectPool.Config config) {
super(objFactory, config);
}
#Override
public Object borrowObject(Object key) throws Exception {
return super.borrowObject(key);
}
#Override
public void returnObject(Object key, Object obj) throws Exception {
super.returnObject(key, obj);
}
}
And in your class that require a JAXB Unmarshaller:
// Autowiring of the Pool
#Resource(name = "unmarshallerPool")
private UnmarshallerPool unmarshallerPool;
public void myMethod() {
Unmarshaller u = null;
try {
// Borrow an Unmarshaller from the pool
u = (Unmarshaller) this.unmarshallerPool.borrowObject(MyJAXBClass.class);
MyJAXBClass myJAXBObject = (MyJAXBClass) u.unmarshal(url);
// Do whatever
} catch (Exception e) {
// Deal with that error
} finally {
try {
// Return the Unmarshaller to the pool
this.unmarshallerPool.returnObject(MyJAXBClass.class, u);
} catch (Exception ignore) {
}
}
}
This example is naive as it uses only one Class to create the JAXBContext and uses the same Class instance as the Key for the Keyed Pool. This can be improved by passing an Array of Classes as parameter rather than only one Class.
Hope this can help.

The creation of unmarshallers is intended to be light. I would recommend doing some profiling before developing a pooling strategy.

Related

What would be the Alternative for SimpleNamingContextBuilder, since it is depricated?

I have a Naming Builder class which registers the objects in the JNDI directory from a map. it is recommended by Spring to replace its own deprecated JNDI Mock implementation. Deprecated as of Spring Framework 5.2 in favor of complete solutions from third parties such as Simple-JNDI
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
public class SMNContextBuilder implements InitializingBean{
private Map ncMap;
public void afterPropertiesSet() throws Exception {
if (ncMap == null) {
throw new IllegalStateException("ncMap is null!");
}
SimpleNamingContextBuilder.emptyActivatedContextBuilder();
bindObjects();
}
protected void bindObjects() {
for (Iterator iter = ncMap.entrySet().iterator(); iter
.hasNext();) {
Map.Entry entryTmp = (Map.Entry) iter.next();
SimpleNamingContextBuilder.getCurrentContextBuilder().bind(
"" + entryTmp.getKey(), entryTmp.getValue());
}
}
public void setNamingContextMap(Map ncMapPar) {
ncMap = ncMapPar;
}
}
Then, I have a Test Config where this is used.
#Configuration
public class MQTestConfig {
#Bean
public SMNContextBuilder jnidInitializingBean() throws JMSException {
SMNContextBuilder builder = new SMNContextBuilder ();
Map<String, Object> map = new HashMap<>();
map.put("java:comp/env/jms/My_ConFac", myConnectionFactory());
map.put("jms/My_Queue", myQueue());
builder.setNamingContextMap(map);
return builder;
}
What would be the Alternative of using SimpleNamingContextBuilder?
I have tried the following:
import javax.naming.InitialContext;
public class SMNContextBuilder implements InitializingBean{
private Map ncMap;
InitialContext ctx;
public SimpleMapNamingContextBuilder() {
try {
this.ctx = new InitialContext();
} catch (NamingException e) {
e.printStackTrace();
}
}
public void afterPropertiesSet() throws Exception {
if (ncMap == null) {
throw new IllegalStateException("ncMap is null!");
}
SMNContextBuilder.emptyActivatedContextBuilder();
bindObjects();
}
protected void bindObjects() {
for (Iterator iter = ncMap.entrySet().iterator(); iter
.hasNext();) {
Map.Entry entryTmp = (Map.Entry) iter.next();
try {
ctx.bind(
"" + entryTmp.getKey(), entryTmp.getValue());
} catch (NamingException e) {
e.printStackTrace();
}
}
}
public void setNamingContextMap(Map ncMapPar) {
ncMap = ncMapPar;
}
}
As you point out in the question, Spring's recommendation is to use Simple-JNDI.
Combing through the documentation, it doesn't look like there is a way to easily replace a class from this library into your current code, but it appears you can still accomplish what you want by loading your beans (in this case myConnectionFactory() and myQueue() into your InitialContext using Properties. See this section of the documentation.
It does seem like the more common avenue is to configure your JNDI resources using .xml/.properties/.ini files. There is a lot of documentation on how to do this in the Simple-JNDI github page (linked above)

Implement AttributeConverter for Generic Class with #Converter

How do I implement AttributeConverter for Generics?
Something Like
class JSONConverter<T> implements AtttributeConverter<T,String>{
//Here How do I get the generic class type with which I can convert a serialized object
}
call the converter in an entity class as
#Column
#Convert( converter = JSONConverter.class) //How do I pass the Generic here
private SomeClass sm;
Can be done even a bit simpler, because you don't necessarily need to pass typeReference via constructor:
public abstract class JsonConverter<T> implements AttributeConverter<T, String> {
private final TypeReference<T> typeReference = new TypeReference<T>() {};
#Resource
private ObjectMapper objectMapper;
#Override
public String convertToDatabaseColumn(T object) {
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
#Override
public T convertToEntityAttribute(String json) {
try {
return objectMapper.readValue(json, typeReference);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
and then class, that extends your JsonConventer can look like:
public class SomeClassConverter extends JsonConverter<SomeClass> { }
I used the following solution with Eclipselink and Java 8. One issue that wasn't immediately apparent is that the converter class must implement AttributeConverter directly (at least for it to work with Eclipselink)
Step 1. Define a generic interface to implement Object <-> String Json conversion. Because interfaces cannot contain properties I defined two methods getInstance() and getObjectMapper() to provide the conversion logic access to object instances that it requires at run time. Converter classes will need to provide implementations for these methods.
package au.com.sfamc.fusion.commons.jpa;
import java.io.IOException;
import javax.persistence.AttributeConverter;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public interface GenericJsonAttributeConverter<X> extends AttributeConverter<X, String> {
X getInstance();
ObjectMapper getObjectMapper();
#Override
default String convertToDatabaseColumn(X attribute) {
String jsonString = "";
try {
// conversion of POJO to json
if(attribute != null) {
jsonString = getObjectMapper().writeValueAsString(attribute);
} else {
jsonString = "{}"; // empty object to protect against NullPointerExceptions
}
} catch (JsonProcessingException ex) {
}
return jsonString;
}
#Override
default X convertToEntityAttribute(String dbData) {
X attribute = null;
try {
if(StringUtils.isNoneBlank(dbData)) {
attribute = getObjectMapper().readValue(dbData, (Class<X>)getInstance().getClass());
} else {
attribute = getObjectMapper().readValue("{}", (Class<X>)getInstance().getClass());
}
} catch (IOException ex) {
}
return attribute;
}
}
Step 2. The getObjectMapper() method implementation would be repeated every converter class so I introduced an abstract class that extends GenericJsonAttributeConverter to save having to implement this method in every converter class.
package au.com.sfamc.fusion.commons.jpa;
import com.fasterxml.jackson.databind.ObjectMapper;
public abstract class AbstractGenericJsonAttributeConverter<X> implements GenericJsonAttributeConverter<X> {
private static final ObjectMapper objectmapper = new ObjectMapper();
#Override
public ObjectMapper getObjectMapper() {
return AbstractGenericJsonAttributeConverter.objectmapper;
}
}
Step 3. Create a concrete implementation of AbstractGenericJsonAttributeConverter for each class you want to to convert to and from Json, even though each class you want to convert will need its own concrete converter class at least you aren't duplicating the conversion code...
package au.com.sfamc.fusion.main.client;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import au.com.sfamc.fusion.commons.jpa.AbstractGenericJsonAttributeConverter;
#Converter
public class ProjectMetricsReportJsonConverter extends AbstractGenericJsonAttributeConverter<ProjectMetricsReport> implements AttributeConverter<ProjectMetricsReport, String> {
private static final ProjectMetricsReport projectMetricsReport = new ProjectMetricsReport();
#Override
public ProjectMetricsReport getInstance() {
return ProjectMetricsReportJsonConverter.projectMetricsReport;
}
}
Note: Now the trick to get this to work with Eclipselink is subtle but required. Along with extending AbstractGenericJsonAttributeConverter the concrete implementation must also make a direct implements reference to the `AttributeConverter' interface (a small price to pay to get generic conversion working)
This can be done a lot simpler.
This example is using Hibernate 5.6.4, Jackson 2.13.1, and Spring DI.
public abstract class JsonConverter<T> implements AttributeConverter<T, String> {
private TypeReference<T> typeReference;
#Resource
private ObjectMapper objectMapper;
public JsonConverter(TypeReference<T> typeReference) {
this.typeReference = typeReference;
}
#Override
public String convertToDatabaseColumn(T object) {
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
#Override
public T convertToEntityAttribute(String json) {
try {
return objectMapper.readValue(json, typeReference);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
and then to use it just extend JsonConverter and provide a TypeReference e.g.
public class SomeClassConverter extends JsonConverter<SomeClass> {
public SomeClassConverter() {
super(new TypeReference<SomeClass>() {
});
}
}

Jetty Websocket Compilation Errors

I am trying to do an Jetty Web Socket example .
I copied a example from internet , which was working fine when i deployed directly into server without making any chnages .
But when i copied the Source (the servlet) into Eclipse IDE , it was giving Compilation
Exceptions related to
The method onClose(int, String) of type Html5Servlet.StockTickerSocket must override a superclass method
- The method onOpen(WebSocket.Connection) of type Html5Servlet.StockTickerSocket must override a superclass method
The method onMessage(String) of type Html5Servlet.StockTickerSocket must override a superclass method
This is my servlet , i kept the jars as it is mentioned in that example
package org.ajeesh.app;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
public class Html5Servlet extends WebSocketServlet {
private AtomicInteger index = new AtomicInteger();
private static final List<String> tickers = new ArrayList<String>();
static{
tickers.add("ajeesh");
tickers.add("peeyu");
tickers.add("kidillan");
tickers.add("entammo");
}
/**
*
*/
private static final long serialVersionUID = 1L;
public WebSocket doWebSocketConnect(HttpServletRequest req, String resp) {
System.out.println("On server");
return new StockTickerSocket();
}
protected String getMyJsonTicker(){
StringBuilder start=new StringBuilder("{");
start.append("\"stocks\":[");
int counter=0;
for (String aTicker : tickers) {
counter++;
start.append("{ \"ticker\":\""+aTicker +"\""+","+"\"price\":\""+index.incrementAndGet()+"\" }");
if(counter<tickers.size()){
start.append(",");
}
}
start.append("]");
start.append("}");
return start.toString();
}
public class StockTickerSocket implements WebSocket.OnTextMessage{
private Connection connection;
private Timer timer;
#Override
public void onClose(int arg0, String arg1) {
System.out.println("Web socket closed!");
}
#Override
public void onOpen(Connection connection) {
System.out.println("onOpen!");
this.connection=connection;
this.timer=new Timer();
}
#Override
public void onMessage(String data) {
System.out.println("onMessage!");
if(data.indexOf("disconnect")>=0){
connection.close();
timer.cancel();
}else{
sendMessage();
}
}
private void sendMessage() {
System.out.println("sendMessage!");
if(connection==null||!connection.isOpen()){
System.out.println("Connection is closed!!");
return;
}
timer.schedule(new TimerTask() {
#Override
public void run() {
try{
System.out.println("Running task");
connection.sendMessage(getMyJsonTicker());
}
catch (IOException e) {
e.printStackTrace();
}
}
}, new Date(),5000);
}
}
}

How to define a custom CamelBeanPostProcessor with Camel 2.9

Camel uses the CamelBeanPostProcessor class to support autowiring of camel endpoints, producer templates etc into beans using annotations.
The documentation for the class says:
If you use the element in your Spring XML then one of
these bean post processors is implicitly installed and configured for
you. So you should never have to explicitly create or configure one of
these instances.
but no mention on how to actually provide a custom implementation.
I use Spring and the <camelContext...> way of configuring camel. How do I provide my own implementation of CamelBeanPostProcessor, that way? The reason is I want to override the canPostProcessBean method to exclude a couple of bean types as post processing them causes a problem for us. They will never have any camel features in them, so it is not a problem.
I think the solution is to define your own BeanPostProcessor. As stated by the javadoc:
ApplicationContexts can autodetect BeanPostProcessor beans in their
bean definitions and apply them to any beans subsequently created.
Plain bean factories allow for programmatic registration of
post-processors, applying to all beans created through this factory.
So, if you use the camel xml like this:
<camel:beanPostProcessor/>
Just replace with:
<bean id="camel:beanPostProcessor"
class="org.mael.sample.camel.postprocessor.CustomCamelBeanPostProcessor" />
This way you will not use the CamelBeanPostProcessor registered by the xml, but your own BeanPostProcessor implementation.
But beware if you want to extend the CamelBeanPostProcessor class, I have seen the source and it uses an instance of org.apache.camel.impl.DefaultCamelBeanPostProcessor as a delegate and extend the later will cause some errors because the exceptions thrown do not match with the exceptions declared in the Spring interfaces.
The best option would be to use also a delegate in your own implementation and just override the methods you want i.e. canPostProcessBean
EDIT:
Here is something I did, check for the TODO comment for applying your functionality, also, pastebin:
package org.mike.sample.camel.postprocessor;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.annotation.XmlTransient;
import org.apache.camel.CamelContext;
import org.apache.camel.Endpoint;
import org.apache.camel.Service;
import org.apache.camel.core.xml.CamelJMXAgentDefinition;
import org.apache.camel.impl.CamelPostProcessorHelper;
import org.apache.camel.impl.DefaultCamelBeanPostProcessor;
import org.apache.camel.spring.GenericBeansException;
import org.apache.camel.util.ServiceHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CustomCamelBeanPostProcessor implements BeanPostProcessor,
ApplicationContextAware {
private static final transient Logger LOG = LoggerFactory
.getLogger(CustomCamelBeanPostProcessor.class);
#XmlTransient
Set<String> prototypeBeans = new LinkedHashSet<String>();
#XmlTransient
private CamelContext camelContext;
#XmlTransient
private ApplicationContext applicationContext;
#XmlTransient
private String camelId;
// must use a delegate, as we cannot extend DefaultCamelBeanPostProcessor,
// as this will cause the
// XSD schema generator to include the DefaultCamelBeanPostProcessor as a
// type, which we do not want to
#XmlTransient
private final DefaultCamelBeanPostProcessor delegate = new DefaultCamelBeanPostProcessor() {
#Override
public CamelContext getOrLookupCamelContext() {
if (camelContext == null) {
if (camelId != null) {
LOG.trace(
"Looking up CamelContext by id: {} from Spring ApplicationContext: {}",
camelId, applicationContext);
camelContext = applicationContext.getBean(camelId,
CamelContext.class);
} else {
// lookup by type and grab the single CamelContext if exists
LOG.trace(
"Looking up CamelContext by type from Spring ApplicationContext: {}",
applicationContext);
Map<String, CamelContext> contexts = applicationContext
.getBeansOfType(CamelContext.class);
if (contexts != null && contexts.size() == 1) {
camelContext = contexts.values().iterator().next();
}
}
}
return camelContext;
}
#Override
public boolean canPostProcessBean(Object bean, String beanName) {
// the JMXAgent is a bit strange and causes Spring issues if we let
// it being
// post processed by this one. It does not need it anyway so we are
// good to go.
// We should also avoid to process the null object bean (in Spring
// 2.5.x)
// TODO - DO YOUR STUFF HERE STRELOK
if (bean == null || bean instanceof CamelJMXAgentDefinition) {
return false;
}
return super.canPostProcessBean(bean, beanName);
}
#Override
public CamelPostProcessorHelper getPostProcessorHelper() {
// lets lazily create the post processor
if (camelPostProcessorHelper == null) {
camelPostProcessorHelper = new CamelPostProcessorHelper() {
#Override
public CamelContext getCamelContext() {
// lets lazily lookup the camel context here
// as doing this will cause this context to be started
// immediately
// breaking the lifecycle ordering of different camel
// contexts
// so we only want to do this on demand
return delegate.getOrLookupCamelContext();
}
#Override
protected RuntimeException createProxyInstantiationRuntimeException(
Class<?> type, Endpoint endpoint, Exception e) {
return new BeanInstantiationException(type,
"Could not instantiate proxy of type "
+ type.getName() + " on endpoint "
+ endpoint, e);
}
protected boolean isSingleton(Object bean, String beanName) {
// no application context has been injected which means
// the bean
// has not been enlisted in Spring application context
if (applicationContext == null || beanName == null) {
return super.isSingleton(bean, beanName);
} else {
return applicationContext.isSingleton(beanName);
}
}
protected void startService(Service service, Object bean,
String beanName) throws Exception {
if (isSingleton(bean, beanName)) {
getCamelContext().addService(service);
} else {
// only start service and do not add it to
// CamelContext
ServiceHelper.startService(service);
if (prototypeBeans.add(beanName)) {
// do not spam the log with WARN so do this only
// once per bean name
CustomCamelBeanPostProcessor.LOG
.warn("The bean with id ["
+ beanName
+ "] is prototype scoped and cannot stop the injected service when bean is destroyed: "
+ service
+ ". You may want to stop the service manually from the bean.");
}
}
}
};
}
return camelPostProcessorHelper;
}
};
public CustomCamelBeanPostProcessor() {
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
try {
return delegate.postProcessBeforeInitialization(bean, beanName);
} catch (Exception e) {
// do not wrap already beans exceptions
if (e instanceof BeansException) {
throw (BeansException) e;
}
throw new GenericBeansException("Error post processing bean: "
+ beanName, e);
}
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
try {
return delegate.postProcessAfterInitialization(bean, beanName);
} catch (Exception e) {
// do not wrap already beans exceptions
if (e instanceof BeansException) {
throw (BeansException) e;
}
throw new GenericBeansException("Error post processing bean: "
+ beanName, e);
}
}
// Properties
// -------------------------------------------------------------------------
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public CamelContext getCamelContext() {
return camelContext;
}
public void setCamelContext(CamelContext camelContext) {
this.camelContext = camelContext;
}
public String getCamelId() {
return camelId;
}
public void setCamelId(String camelId) {
this.camelId = camelId;
}
}

Access properties file programmatically with Spring?

We use the code below to inject Spring beans with properties from a properties file.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:/my.properties"/>
</bean>
<bean id="blah" class="abc">
<property name="path" value="${the.path}"/>
</bean>
Is there a way we can access the properties programmatically? I'm trying to do some code without dependency injection. So I'd like to just have some code like this:
PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer();
props.load("classpath:/my.properties");
props.get("path");
How about PropertiesLoaderUtils?
Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
If all you want to do is access placeholder value from code, there is the #Value annotation:
#Value("${settings.some.property}")
String someValue;
To access placeholders From SPEL use this syntax:
#('${settings.some.property}')
To expose configuration to views that have SPEL turned off, one can use this trick:
package com.my.app;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.stereotype.Component;
#Component
public class PropertyPlaceholderExposer implements Map<String, String>, BeanFactoryAware {
ConfigurableBeanFactory beanFactory;
#Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
protected String resolveProperty(String name) {
String rv = beanFactory.resolveEmbeddedValue("${" + name + "}");
return rv;
}
#Override
public String get(Object key) {
return resolveProperty(key.toString());
}
#Override
public boolean containsKey(Object key) {
try {
resolveProperty(key.toString());
return true;
}
catch(Exception e) {
return false;
}
}
#Override public boolean isEmpty() { return false; }
#Override public Set<String> keySet() { throw new UnsupportedOperationException(); }
#Override public Set<java.util.Map.Entry<String, String>> entrySet() { throw new UnsupportedOperationException(); }
#Override public Collection<String> values() { throw new UnsupportedOperationException(); }
#Override public int size() { throw new UnsupportedOperationException(); }
#Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); }
#Override public void clear() { throw new UnsupportedOperationException(); }
#Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
#Override public String remove(Object key) { throw new UnsupportedOperationException(); }
#Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}
And then use the exposer to expose properties to a view:
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
<property name="attributesMap">
<map>
<entry key="config">
<bean class="com.my.app.PropertyPlaceholderExposer" />
</entry>
</map>
</property>
</bean>
Then in view, use the exposed properties like this:
${config['settings.some.property']}
This solution has the advantage that you can rely on standard placeholder
implementation injected by the context:property-placeholder tag.
Now as a final note, if you really need a to capture all placeholder properties and their values, you have to pipe them through StringValueResolver to make sure that placeholders work inside the property values as expected. The following code will do that.
package com.my.app;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.StringValueResolver;
public class AppConfig extends PropertyPlaceholderConfigurer implements Map<String, String> {
Map<String, String> props = new HashMap<String, String>();
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException {
this.props.clear();
for (Entry<Object, Object> e: props.entrySet())
this.props.put(e.getKey().toString(), e.getValue().toString());
super.processProperties(beanFactory, props);
}
#Override
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
super.doProcessProperties(beanFactoryToProcess, valueResolver);
for(Entry<String, String> e: props.entrySet())
e.setValue(valueResolver.resolveStringValue(e.getValue()));
}
// Implement map interface to access stored properties
#Override public Set<String> keySet() { return props.keySet(); }
#Override public Set<java.util.Map.Entry<String, String>> entrySet() { return props.entrySet(); }
#Override public Collection<String> values() { return props.values(); }
#Override public int size() { return props.size(); }
#Override public boolean isEmpty() { return props.isEmpty(); }
#Override public boolean containsValue(Object value) { return props.containsValue(value); }
#Override public boolean containsKey(Object key) { return props.containsKey(key); }
#Override public String get(Object key) { return props.get(key); }
#Override public void clear() { throw new UnsupportedOperationException(); }
#Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
#Override public String remove(Object key) { throw new UnsupportedOperationException(); }
#Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}
I have done this and it has worked.
Properties props = PropertiesLoaderUtils.loadAllProperties("my.properties");
PropertyPlaceholderConfigurer props2 = new PropertyPlaceholderConfigurer();
props2.setProperties(props);
That should work.
CREDIT: Programmatic access to properties in Spring without re-reading the properties file
I've found a nice implementation of accessing the properties programmatically in spring without reloading the same properties that spring has already loaded. [Also, It is not required to hardcode the property file location in the source]
With these changes, the code looks cleaner & more maintainable.
The concept is pretty simple. Just extend the spring default property placeholder (PropertyPlaceholderConfigurer) and capture the properties it loads in the local variable
public class SpringPropertiesUtil extends PropertyPlaceholderConfigurer {
private static Map<String, String> propertiesMap;
// Default as in PropertyPlaceholderConfigurer
private int springSystemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;
#Override
public void setSystemPropertiesMode(int systemPropertiesMode) {
super.setSystemPropertiesMode(systemPropertiesMode);
springSystemPropertiesMode = systemPropertiesMode;
}
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
super.processProperties(beanFactory, props);
propertiesMap = new HashMap<String, String>();
for (Object key : props.keySet()) {
String keyStr = key.toString();
String valueStr = resolvePlaceholder(keyStr, props, springSystemPropertiesMode);
propertiesMap.put(keyStr, valueStr);
}
}
public static String getProperty(String name) {
return propertiesMap.get(name).toString();
}
}
Usage Example
SpringPropertiesUtil.getProperty("myProperty")
Spring configuration changes
<bean id="placeholderConfigMM" class="SpringPropertiesUtil">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="locations">
<list>
<value>classpath:myproperties.properties</value>
</list>
</property>
</bean>
Hope this helps to solve the problems you have
You can also use either the spring utils, or load properties via the PropertiesFactoryBean.
<util:properties id="myProps" location="classpath:com/foo/myprops.properties"/>
or:
<bean id="myProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:com/foo/myprops.properties"/>
</bean>
Then you can pick them up in your application with:
#Resource(name = "myProps")
private Properties myProps;
and additionally use these properties in your config:
<context:property-placeholder properties-ref="myProps"/>
This is also in the docs: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-util-properties
Create a class like below
package com.tmghealth.common.util;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
#Component
#Configuration
#PropertySource(value = { "classpath:/spring/server-urls.properties" })
public class PropertiesReader extends PropertyPlaceholderConfigurer {
#Override
protected void processProperties(
ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException {
super.processProperties(beanFactory, props);
}
}
Then wherever you want to access a property use
#Autowired
private Environment environment;
and getters and setters then access using
environment.getProperty(envName
+ ".letter.fdi.letterdetails.restServiceUrl");
-- write getters and setters in the accessor class
public Environment getEnvironment() {
return environment;
}`enter code here`
public void setEnvironment(Environment environment) {
this.environment = environment;
}
You can get your properties through Environment class. As documentation stands:
Properties play an important role in almost all applications, and may originate from a variety of sources: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps, and so on. The role of the environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them.
Having Environment as a env variable, simply call:
env.resolvePlaceholders("${your-property:default-value}")
You can get your 'raw' properties through:
env.getProperty("your-property")
It will search through all properties source that spring has registered.
You can either obtain Environment through:
inject ApplicationContext by implementing ApplicationContextAware and then call getEnvironment() on context
implement EnvironmentAware.
It's obtain through implementation of a class because properties are resolved on early stage of application startup, as they may be required for bean construction.
Read more on documentation: spring Environment documentation
As you know the newer versions of Spring don't use the PropertyPlaceholderConfigurer and now use another nightmarish construct called PropertySourcesPlaceholderConfigurer. If you're trying to get resolved properties from code, and wish the Spring team gave us a way to do this a long time ago, then vote this post up! ... Because this is how you do it the new way:
Subclass PropertySourcesPlaceholderConfigurer:
public class SpringPropertyExposer extends PropertySourcesPlaceholderConfigurer {
private ConfigurableListableBeanFactory factory;
/**
* Save off the bean factory so we can use it later to resolve properties
*/
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
super.processProperties(beanFactoryToProcess, propertyResolver);
if (beanFactoryToProcess.hasEmbeddedValueResolver()) {
logger.debug("Value resolver exists.");
factory = beanFactoryToProcess;
}
else {
logger.error("No existing embedded value resolver.");
}
}
public String getProperty(String name) {
Object propertyValue = factory.resolveEmbeddedValue(this.placeholderPrefix + name + this.placeholderSuffix);
return propertyValue.toString();
}
}
To use it, make sure to use your subclass in your #Configuration and save off a reference to it for later use.
#Configuration
#ComponentScan
public class PropertiesConfig {
public static SpringPropertyExposer commonEnvConfig;
#Bean(name="commonConfig")
public static PropertySourcesPlaceholderConfigurer commonConfig() throws IOException {
commonEnvConfig = new SpringPropertyExposer(); //This is a subclass of the return type.
PropertiesFactoryBean commonConfig = new PropertiesFactoryBean();
commonConfig.setLocation(new ClassPathResource("META-INF/spring/config.properties"));
try {
commonConfig.afterPropertiesSet();
}
catch (IOException e) {
e.printStackTrace();
throw e;
}
commonEnvConfig.setProperties(commonConfig.getObject());
return commonEnvConfig;
}
}
Usage:
Object value = PropertiesConfig.commonEnvConfig.getProperty("key.subkey");
This help me:
ApplicationContextUtils.getApplicationContext().getEnvironment()
Here is another sample .
XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
cfg.postProcessBeanFactory(factory);
This will resolve any nested properties.
public class Environment extends PropertyPlaceholderConfigurer {
/**
* Map that hold all the properties.
*/
private Map<String, String> propertiesMap;
/**
* Iterate through all the Property keys and build a Map, resolve all the nested values before building the map.
*/
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
super.processProperties(beanFactory, props);
propertiesMap = new HashMap<String, String>();
for (Object key : props.keySet()) {
String keyStr = key.toString();
String valueStr = beanFactory.resolveEmbeddedValue(placeholderPrefix + keyStr.trim() + DEFAULT_PLACEHOLDER_SUFFIX);
propertiesMap.put(keyStr, valueStr);
}
}
/**
* This method gets the String value for a given String key for the property files.
*
* #param name - Key for which the value needs to be retrieved.
* #return Value
*/
public String getProperty(String name) {
return propertiesMap.get(name).toString();
}
This post also explatis howto access properties: http://maciej-miklas.blogspot.de/2013/07/spring-31-programmatic-access-to.html
You can access properties loaded by spring property-placeholder over such spring bean:
#Named
public class PropertiesAccessor {
private final AbstractBeanFactory beanFactory;
private final Map<String,String> cache = new ConcurrentHashMap<>();
#Inject
protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public String getProperty(String key) {
if(cache.containsKey(key)){
return cache.get(key);
}
String foundProp = null;
try {
foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");
cache.put(key,foundProp);
} catch (IllegalArgumentException ex) {
// ok - property was not found
}
return foundProp;
}
}
This is the finest way I got it to work:
package your.package;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
public class ApplicationProperties {
private Properties properties;
public ApplicationProperties() {
// application.properties located at src/main/resource
Resource resource = new ClassPathResource("/application.properties");
try {
this.properties = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException ex) {
Logger.getLogger(ApplicationProperties.class.getName()).log(Level.SEVERE, null, ex);
}
}
public String getProperty(String propertyName) {
return this.properties.getProperty(propertyName);
}
}
create .properties file in classpath of your project and add path configuration in xml`<context:property-placeholder location="classpath*:/*.properties" />`
in servlet-context.xml after that u can directly use your file everywhere
Please use the below code in your spring configuration file to load the file from class path of your application
<context:property-placeholder
ignore-unresolvable="true" ignore-resource-not-found="false" location="classpath:property-file-name" />
I know this is an old thread, however, this topic in my opinion becomes of great importance for those using the functional approach for all those usecases where you need a microservice that loads "instantly" and therefore you avoid using annotations.
The problem that remained unsolved was to load eventually the environment variables which I had in my application.yml.
public class AppPropsLoader {
public static Properties load() {
var propPholderConfig = new PropertySourcesPlaceHolderConfigurer();
var yaml = new YamlPropertiesFactoryBean();
ClassPathResource resource = new ClassPathResource("application.yml");
Objects.requireNonNull(resource, "File application.yml does not exist");
yaml.setResources(resource);
Objects.requireNonNull(yaml.getObject(), "Configuration cannot be null");
propPholderConfig.postProcessBeanFactory(new DefaultListableBeanFactory());
propPholderConfig.setProperties(yaml.getObject());
PropertySources appliedPropertySources =
propPholderConfig.getAppliedPropertySources();
var resolver = new PropertySourcesPlaceholderResolver(appliedPropertySources);
Properties resolvedProps = new Properties();
for (Map.Entry<Object, Object> prop: yaml.getObject().entrySet()) {
resolvedProps.setProperty((String)prop.getKey(),
getPropertyValue(resolver.resolvePlaceHolders(prop.getValue()));
}
return resolvedProps;
}
static String getPropertyValue(Object prop) {
var val = String.valueOf(prop);
Pattern p = Pattern.compile("^(\\$\\{)([a-zA-Z0-9-._]+)(\\})$");
Matcher m = p.matcher(val);
if(m.matches()) {
return System.getEnv(m.group(2));
}
return val;
}
}

Resources