I'm using Spring 4.1.6.
I have something like the following:
foo.properties:
valueX=a
valueY=b
Spring bean:
<context:property-placeholder location="classpath:foo.properties" ignore-unresolvable="false" ignore-resource-not-found="false" />
<bean id="foo" class="com.foo.bar.MyClass" >
<property name="someValue" value="${valueX}" />
</bean>
I have a non-Spring class which also needs to use a value from foo.properties.
Non Spring Class:
public void doSomething() {
String valueY = System.getProperty("valueY");
}
When Spring loads foo.properties, is there a way to populate all the properties into System properties so that I can get "valueY" using System.getProperty("valueY").
I don't want to load foo.properties again in my non-Spring class.
The context:property-placeholder will create a PropertySourcesPlaceholderConfigurer config bean for you. You cannot access the properties from this bean programatically as stated here.
What you can do is to load the properties into a separate spring bean as given below.
#Bean(name = "mapper")
public PropertiesFactoryBean mapper() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("application.properties"));
return bean;
}
and then set the system property when the context load is finished using a listener as given below. Got the code from this answer
#Component
public class YourJobClass implements ApplicationListener<ContextRefreshedEvent> {
#Resource(name = "mapper")
private Properties myTranslator;
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
System.setProperties(myTranslator);
}
}
Related
I am migrating an application from an XmlWebApplicationContext to a pure java configuration solution using AnnotationConfigApplicationContext. I am having a problem reusing existing xml configuration files via #ImportResource. We are using spring 3.2.11.
When I use the xml based context, beans defined in the xml files that are java configuration (#Configuration) are automatically picked up by the context and any beans they define are visible. However, when imported through #ImportResource, #Beans in the configuration objects are not created.
Here is a unit test that illustrates my problem:
XmlConfigTest.java
#Test
public void testAnnotationContext()
{
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(XmlFromJava.class);
ctx.refresh();
assertEquals("xml value", ctx.getBean("xmlBean", String.class));
assertEquals("nested value", ctx.getBean("nestedBean", String.class));
}
#Test
public void testXmlContext()
{
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:test.xml");
ctx.refresh();
assertEquals("xml value", ctx.getBean("xmlBean", String.class));
// fails here
assertEquals("nested value", ctx.getBean("nestedBean", String.class));
}
#Configuration
#ImportResource("classpath:test.xml")
public static class XmlFromJava { }
NestedConfig.java
#Configuration
public class NestedConfig
{
#Bean
public String nestedBean()
{
return "nested value";
}
}
test.xml
<context:annotation-config/>
<bean class="NestedConfig"/>
<bean name="xmlBean" class="java.lang.String">
<constructor-arg value="xml value"/>
</bean>
I would expect the bean 'nestedBean' to exist from the NestedConfig class. testAnnotationContext() fails to load the 'nestedBean' but testXmlContext() works.
I have the following situation:
#Controller
public class myController {
#Autowired
private IProxy service;
public ModelAndView init(HttpServletRequest request, HttpServletResponse response) throws Exception {
List<String> list = service.getName();
}
}
Then my Service is define as follow:
public interface IProxy {
public List<String> getName();
}
Proxy class is responsible for the lookup to the remote bean
#Service("service")
public class Proxy implements IProxy {
...
public List<String> getName() {
return myClass.getName();
}
And the implementation is the following:
#Interceptors(interceptor.class)
#Stateless
#Resource(name = "java:/db")
#Remote(MyClassRemote.class)
public class MyClassImpl extends MyEjb implements MyClassRemote{
#PersistenceContext(unitName = "db")
private EntityManager em;
#Resource
private SessionContext sctx;
#Autowired
public IMyRepo myRepo;
#Override
public List<String> getName() {
try {
return myRepo.getName(em);
}
catch (Exception ex) {
ex.printStackTrace();
throw ex;
}
finally {}
}
So, the problem is that here myRepo is null. I don't know why because IMyRepo and his implementation are always located within the path scanned by Spring.
Just one clarification: MyRepo class that implements IMyRepo is annotated with #Repository.
Any idea?
you can inject spring beans in EJB using Spring interceptors, as explained here in the official documentation. Basically you'll need to adjust your class as follows:
// added the SpringBeanAutowiringInterceptor class
#Interceptors({ interceptor.class, SpringBeanAutowiringInterceptor.class })
#Stateless
#Resource(name = "java:/db")
#Remote(MyClassRemote.class)
public class MyClassImpl extends MyEjb implements MyClassRemote{
// your code
}
You'll also need to define the context location in a beanRefContext.xml file (with your own application context file):
application-context.xml version
<bean id="context"
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>application-context.xml</value>
</list>
</constructor-arg>
</bean>
Java Configuration version:
<bean id="context"
class="org.springframework.context.annotation.AnnotationConfigApplicationContext">
<constructor-arg>
<list>
<value type="java.lang.Class">com.your.app.Configuration</value>
</list>
</constructor-arg>
</bean>
Spring beans and EJB are two different things, you can't just inject a Spring bean in an EJB, because that EJB is no Spring bean, so Spring doesn't know there is a field which should be injected by Spring (unless you use some fancy AOP stuff, which can enable injection into non-Spring-managed beans).
I have a Spring boot application that needs to perform LDAP queries. I'm trying to take the following recommendation from the Spring boot documentation:
"Many Spring configuration examples have been published on the
Internet that use XML configuration. Always try to use the equivalent
Java-base configuration if possible."
In a Spring XML configuration file, I would have used:
<ldap:context-source
url="ldap://localhost:389"
base="cn=Users,dc=test,dc=local"
username="cn=testUser"
password="testPass" />
<ldap:ldap-template id="ldapTemplate" />
<bean id="personRepo" class="com.llpf.ldap.PersonRepoImpl">
<property name="ldapTemplate" ref="ldapTemplate" />
</bean>
How would I configure this using a Java-based configuration? I need to be able to change URL, base, username, and password attributes of ldap:context-source without a code rebuild.
The <ldap:context-source> XML tag produces an LdapContextSource bean and the <ldap:ldap-template> XML tag produces an LdapTemplate bean so that's what you need to do in your Java configuration:
#Configuration
#EnableAutoConfiguration
#EnableConfigurationProperties
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
#ConfigurationProperties(prefix="ldap.contextSource")
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate(ContextSource contextSource) {
return new LdapTemplate(contextSource);
}
#Bean
public PersonRepoImpl personRepo(LdapTemplate ldapTemplate) {
PersonRepoImpl personRepo = new PersonRepoImpl();
personRepo.setLdapTemplate(ldapTemplate);
return personRepo;
}
}
To allow you to change the configuration without a rebuild of your code, I've used Spring Boot's #ConfigurationProperties. This will look in your application's environment for properties prefixed with ldap.contextSource and then apply them to the LdapContextSource bean by calling the matching setter methods. To apply the configuration in the question, you can use an application.properties file with four properties:
ldap.contextSource.url=ldap://localhost:389
ldap.contextSource.base=cn=Users,dc=test,dc=local
ldap.contextSource.userDn=cn=testUser
ldap.contextSource.password=testPass
Where is a good place to store custom config info in spring mvc 3.x and how would I access that info globally via any controller?
Is there a built in config manager?
I assume 'custom config' means a configuration file the code reads / you / your operations team can update?
One easy solution is to use spring beans xml configuration file and deploy your war in exploded fashion.
Create a configuration java class:
// File: MyConfig.java ------------------------------------
public class MyConfig {
private String supportEmail;
private String websiteName;
// getters & setters..
}
Configure the class as a Spring bean and set its properties on your spring beans xml file (can also create a new file and use <import resource="..."/>):
// File: root-context.xml ----------------------------------------
<beans ...>
...
<bean class="com.mycompany.MyConfig">
<property name="supportEmail" value="support#mycompany.com"/>
<property name="websiteName" value="Hello Site"/>
</bean>
...
</beans>
Inject your configuration class (eg: in a controller)
// File: HelloController.java ------------------------------------
#Controller
#RequestMapping("/hello")
public class HelloController {
#Autowired MyConfig config;
// ...
}
However updates to the configuration would require re-deploy / server restarts
You can also use <context:property-placeholder>.
It looks like this.
myapp.properties:
foo=bar
spring beans xml:
<context:property-placeholder location="classpath:myapp.properties"/>
Or
<context:property-placeholder location="file:///path/to/myapp.properties"/>
Controller:
import org.springframework.beans.factory.annotation.Value;
...
#Controller
public class Controller {
#Value("${foo}")
private String foo;
If you want to get properties programmatically, you can use Environment with #PropertySource.
Configuration:
#Configuration
#PropertySource("classpath:myapp.properties")
public class AppConfig {
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Controller:
#Controller
public class Controller {
#Value("${foo}")
private String foo;
#Autowired
private Environment env;
#RequestMapping(value = "dosomething")
public String doSomething() {
env.getProperty("foo");
...
}
Hope this helps.
I'm working on an application that uses Spring and slf4j. That application uses more ApplicationContext parallelly.
Is there any way to these different ApplicationContexts use different logging properties?
So the first AC could log into "x.txt" while the second to "y.txt".
I wouldn't like to use more properties file. The appropriate way would be to define a Logger Bean in Spring XML configuration file where I could set different output target for the appropriate property.
For example:
<bean id="LoggerBean" class="???">
<property name="target" value="${target}" />
</bean>
Here I could manipulate the target variable from source, which would be very handy.
private static final Logger log = LoggerFactory.getLogger(MyClass.class);
So LoggerFactory.getLogger would use the LoggerBean bean configuration to instantiate a Logger class.
I need a method where each ApplicationContext has an own LoggerFactory object with different properties (like different target output). So I wouldn't have to rewrite the current code.
I use ApplicationContexts configured by the same xml config file. So these ApplicationContexts use
the same classes. Because of that, all Logger are instantiated from LoggerFactory with the same class names they used in.
All Logger are instantiated by LoggerFactory.getLogger(MyClass.class) form, as those classes are same in all ApplicationContext ("MyClass"), I can't define differently named Loggers.
Thanks for any reply.
You can define a Spring-managed bean that will configure the logger. For example, assuming you are using logback to implement the slf4j API, this class will load a specified logging configuration file into logback after Spring sets its properties:
public class LogBackConfigurer implements InitializingBean {
private Resource location;
public void setLocation(Resource location) {
this.location = location;
}
public void afterPropertiesSet() throws Exception {
JoranConfigurator configurator = new JoranConfigurator();
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
configurator.setContext(loggerContext);
configurator.doConfigure(location.getInputStream());
}
}
In each Spring configuration file you want to have a different logging configuration, define a bean like the following with a different logging configuration file location.
<bean class="com.example.log.LogBackConfigurer">
<property name="location" value="classpath:a-logback.xml"/>
</bean>
The class modifies the single application-wide logging context, which is necessary because you want to call the static Logger factory method in your application code. To ensure the logging configuration files don't step on each other, they each have to define differently named loggers.
The final solution was the following:
SLF4j and Logback support MDC which contains key/value pairs on per thread basis. Although the main advantages for our problem is that a child thread automatically inherits key/value pairs of its parent, so if a new Thread is created during initialization of ApplicationContext, that Thread will inherit those pairs from the calling thread. After that you can include these stored values in log message pattern.
So I put a special ApplicationContext identifier in MDC before loading the ApplicationContext. When classes are instantiated with a Logger field, these fields obtain their unique identifier which is included in the log message pattern.
<Pattern>[%X{contextID}] - [%thread] - %date{dd/MM/yyyy HH:mm:ss} %level %msg%n</Pattern>
LoggerSeparator.java
public class LoggerSeparator implements InitializingBean{
private Integer contextID;
public LoggerSeparator() {}
public void setContextID(int id) {
this.contextID = id;
}
#Override
public void afterPropertiesSet() throws Exception {
if ( contextID != null )
MDC.put("contextID", contextID.toString());
}
}
This bean is the first defined Spring Bean in main.xml.
<bean class="com.myproblem.LoggerSeparator">
<property name="contextID" value="${contextID}" />
</bean>
...
That class set the contextID in MD. The contextID comes from source code.
...
Properties props = new Properties();
props.put("contextID", contextID);
PropertyPlaceholderConfigurer conf = new PropertyPlaceholderConfigurer();
conf.setProperties(props);
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
context.addBeanFactoryPostProcessor(conf);
context.setConfigLocation("beans/main.xml");
context.refresh();
...
The log messages are logged into one file, but now I can separate them by their unique identifier.
You can use custom FactoryBean to add the logger into the context:
public class Slf4jLoggerFactoryBean implements FactoryBean<Logger> {
private String loggerName;
public Logger getObject() throws Exception {
return LoggerFactory.getLogger(loggerName);
}
public Class<?> getObjectType() {
return Logger.class;
}
public boolean isSingleton() {
return true;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
}
And then the XML will look like:
<bean id="LoggerBean" class="com.example.Slf4jLoggerFactoryBean">
<property name="target" value="${target}" />
</bean>