Spring resource class loading from an external folder - spring

I have a spring project name: SpringExample‬
the Directory Structure:
the locale.xml for the spring :
<bean id="customerService" class="com.mkyong.customer.services.CustomerService" />
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>\SpringExample\conf‬\messages</value>
</property>
</bean>
the code for CustomerService:
package com.mkyong.customer.services;
public class CustomerService implements MessageSourceAware
{
private MessageSource messageSource;
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
public void printMessage(){
String name = messageSource.getMessage("customer.name",
new Object[] { 28, "http://www.mkyong.com" }, Locale.US);
System.out.println("Customer name (English) : " + name);
}
the code for the app class (the main class):
package com.mkyong.common;
import java.util.Locale;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mkyong.customer.services.CustomerService;
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "locale.xml" });
// get the message resource inside context
String name = context.getMessage("customer.name", new Object[] { 28,
"http://www.mkyong.com" }, Locale.US);
System.out.println("Customer name (English) : " + name);
// get the message resource inside the bean
CustomerService cust = (CustomerService) context
.getBean("customerService");
cust.printMessage();
}
}
But it fails to bundle the proprtie file, because it isn't in the package.
i get the exception:
WARNING: ResourceBundle [\SpringExample\conf?\messages] not found for
MessageSource: Can't find bundle for base name
\SpringExample\conf?\messages, locale en_US
how can i get it to bundle with external folder resource like in my example?

Make the conf directory a maven resource directory (maven build helper plugin) or move the files from conf to src\main\resources. Then modify the ResourceBundleMessageSource to load the messages form classpath root, because src\main\resources correspond to the classpath root.
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>messages</value>
</property>
</bean>
Attention: If you use ReloadableResourceBundleMessageSource instead of ResourceBundleMessageSource then you have to use the prefix classpath: for the basename propertz value (classpath:messages)

Related

Mixing XML and java configuration

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.

How can we switch between different Implementations in Spring Context XML with an Boolean?

How can we switch between different Implementations in Spring Context XML with an Boolean?
for example:
<bean id="detailsController" class="com.something.detailsController" >
if true then
<property name="dao" ref="firstDao"/>
else
<property name="dao" ref="secoundDao"/>
I know in Spring3 we can work with profiles
You could do that by modifying your Java code and use Spring EL together with ApplicationAware and InitializingBean.
public class DetailsController implements ApplicationContextAware, InitializingBean {
private DetailsControllerDAO dao;
private String daoName;
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void afterPropertiesSet() {
dao = applicationContext.getBean(daoName);
}
public void setDaoName(String daoName) {
this.daoName = daoName;
}
}
In XML:
<bean id="detailsController" class="com.something.detailsController">
<property name="daoName" value="#{myCondition ? 'firstDao' : 'secondDao'}" />
</bean>
Of course, this solution has the disadvantage to add dependency to Spring code in your controller. To avoid that, you could move that code in a proxy class, as described by Guillaume Darmont.
I dont think this can be done at the XML level.
Spring really cannot do that. See the bean lifecycle. Been classes are created, than properties are injected and than afterPropertiesSet() or #PostConstructor methods are invoked. Of course when I omit lazy initialized beans.
But if you want for testing etc. and so you need just the firstDao or the secondDao in your application at the sametime that depends just on your settings, you can use a bean factory. The bean factory creates your bean as you want. I also use it for to split development environment, test environment and production environment.
package com.dummyexample.config;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Factory bean to create real or test dao.
* The result depends on realDaoEnabled configuration parameter.
*
* #author Martin Strejc
*/
#Configuration
public class DaoBeanFactory {
// mapping to servlet context configuration
#Resource(mappedName = "realDaoEnabled")
private Boolean realDaoEnabled = true;
// TestDao extends or implements Dao
#Autowired
private TestDao testDao;
// ProdDao extends or implements Dao
#Autowired
private ProdDao prodDao;
public DaoBeanFactory() {
}
#Bean(name="dao")
public Dao getDao() {
if(realDaoEnabled) {
return prodDao;
}
return testDao;
}
}
Since your DAOs are exchangeable, they inherits the same type (abstract class or interface). Thus you can write a RoutingDetailsControllerDAO.
Let's say that your common interface is named DetailsControllerDAO, with two methods getDetails and getMoreDetails inside, the code would be :
public class RoutingDetailsControllerDAO implements DetailsControllerDAO {
private DetailsControllerDAO firstDAO;
private DetailsControllerDAO secondDAO;
protected DetailsControllerDAO getDAOToUse() {
return YOUR_BOOLEAN_CONDITION ? firstDAO : secondDAO;
}
#Override
public Details getDetails() {
return getDAOToUse().getDetails();
}
#Override
public Details getMoreDetails() {
return getDAOToUse().getMoreDetails();
}
// Insert firstDAO and secondDAO setters below
...
}
Your Spring XML config is now :
<bean id="detailsController" class="com.something.detailsController" >
<property name="dao" ref="routingDetailsControllerDAO"/>
</bean>
<bean id="routingDetailsControllerDAO" class="com.something.RoutingDetailsControllerDAO">
<property name="firstDao" ref="firstDao"/>
<property name="secondDao" ref="secondDao"/>
</bean>
Few possibilities:
You can either use profiles (<beans profiles="profileOne">).
You can use FactoryBean to create the correct DAO
You can use SPeL
The last one is the easiest:
<bean id="detailsController" class="com.something.detailsController">
<property name="dao" ref="#{condition ? 'firstDao' : 'secondDao'}" />
</bean>
Of course you can load bean name from properties file via property configurer:
<bean id="detailsController" class="com.something.detailsController">
<property name="dao" ref="${bean.name.from.properties.file}" />
</bean>

Using values from properties file in spring roo

I'm new to spring and spring-roo. I try to build an application and read some key value pairs from a properties file.
I created a myconfig.properties file and saved it to src/main/resources/META-INF/spring/.
The content of the file is:
## My Configuration settings
myconfig.url=https://1.2.3.4/api.php
myconfig.username=user1
myconfig.password=password1
Now I added a bean configuration into appilcationContext.xml in the same directory
<bean id="MyConfig" class="com.test.client.MyClient">
<property name="url" value="${myconfig.url}" />
<property name="username" value="${myconfig.username}" />
<property name="password" value="${myconfig.password}" />
</bean>
In my class file I tried to access the values, but I get an File not found error
package com.test.client;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.roo.addon.tostring.RooToString;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
#RooJavaBean
#RooToString
#RooJpaActiveRecord
public class MyClient {
private String url;
private String username;
private String password;
public static String login()
{
// Construct the spring application context
AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MyClient config = (MyClient) context.getBean("MyConfig");
// Register hook to shutdown Spring gracefully
// See http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-factory-shutdown
context.registerShutdownHook();
String token = null;
final String url = config.getUrl();
final String username = config.getUsername();
final String password = config.getPassword();
....
Thanks for any help!
Try
AbstractApplicationContext context = new ClassPathXmlApplicationContext("classpath*:META-INF/spring/applicationContext.xml");
However the best practice is to implement interface ApplicationContextAware.
Stefano

ResourceBundle not found for MessageSource when placed inside a folder

I am trying to use resource bundles with Spring's Message Source. Here is the way I am doing it:
#Component
public class MessageResolver implements MessageSourceAware {
#Autowired
private MessageSource messageSource;
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
public String getMessage(){
return messageSource.getMessage("user.welcome", new Object[]{"Rama"} , Locale.US);
}
}
And here is my folder structure:
messages_en_US.properties contains just one line:
user.welcome=Welcome {0}
Here is the xml configuration used:
<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>resourcebundles/messages</value>
</property>
</bean>
Here is the error I am getting:
WARNING: ResourceBundle [resourcebundles/messages] not found for MessageSource: Can't find bundle for base name resourcebundles/messages, locale en_US
Exception in thread "main" org.springframework.context.NoSuchMessageException: No message found under code 'user.welcome' for locale 'en_US'.
But if I move my resource bundle to directly under the resources folder, it is working fine. In this case, here is the xml configuration I am using:
<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>messages</value>
</property>
Is is that if I have to use ResourceBundleMessageSource, I should put my resource bundles directly under the resources? If i have to keep it in specified folder only, is there any other way to get this one work?
Thanks!
boy, maybe you can change the xml configuration as follows:
use
org.springframework.context.support.ReloadableResourceBundleMessageSource
instead of
org.springframework.context.support.ResourceBundleMessageSource
all configuration like this:
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:resourcebundles/messages" />
<property name="useCodeAsDefaultMessage" value="true" />
</bean>
Change your configuration to the following for messageSource bean in your xml file.
<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>classpath*:resourcebundles/messages</value>
</property>
</bean>
Since all your properties files are in classpath of java you need to define the path with prefix classpath*: otherwise it will look into the web directory of your application.
Hope this helps you. Cheers.
It's nearly 2015 now and I'm using Spring 4.1.2.RELEASE and there's definitely a problem with the way the messageSource bean needs to be configured so it picks up the target resource bundle.
1) If the messageSource bean is of type ReloadableResourceBundleMessageSource it won't work:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
#Configuration
#ComponentScan(basePackages = { "com.intertech.service" })
//#ImportResource({"classpath:spring/applicationContext-i18n.xml"})
public class AppConfig {
#Bean(name = "messageSource")
public ReloadableResourceBundleMessageSource getMessageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("config/messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
// #Bean(name = "messageSource")
// public ResourceBundleMessageSource getMessageSource() {
// ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
// messageSource.setBasename("config/messages");
// messageSource.setDefaultEncoding("UTF-8");
// messageSource.setUseCodeAsDefaultMessage(true);
// return messageSource;
// }
}
2) If the messageSource bean is of type ResourceBundleMessageSource it will work:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
#Configuration
#ComponentScan(basePackages = { "com.intertech.service" })
//#ImportResource({"classpath:spring/applicationContext-i18n.xml"})
public class AppConfig {
// #Bean(name = "messageSource")
// public ReloadableResourceBundleMessageSource getMessageSource() {
// ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
// messageSource.setBasename("config/messages");
// messageSource.setDefaultEncoding("UTF-8");
// messageSource.setUseCodeAsDefaultMessage(true);
// return messageSource;
// }
#Bean(name = "messageSource")
public ResourceBundleMessageSource getMessageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("config/messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
}
3) If you're using an XML configuration file combined with a configuration class - it will work (notice how the base bundle is configured in a class like qualification manner i.e. 'config.messages' not 'config/messages'): (applicationContext-i18n.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource"
p:basename="config.messages"
p:useCodeAsDefaultMessage="true"/>
<!-- This will not work -->
<!--
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
p:basename="config/messages"
p:useCodeAsDefaultMessage="true"/>
-->
</beans>
and:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
#Configuration
#ComponentScan(basePackages = { "com.intertech.service" })
#ImportResource({"classpath:spring/applicationContext-i18n.xml"})
public class AppConfig {
// #Bean(name = "messageSource")
// public ReloadableResourceBundleMessageSource getMessageSource() {
// ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
// messageSource.setBasename("config/messages");
// messageSource.setDefaultEncoding("UTF-8");
// messageSource.setUseCodeAsDefaultMessage(true);
// return messageSource;
// }
// #Bean(name = "messageSource")
// public ResourceBundleMessageSource getMessageSource() {
// ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
// messageSource.setBasename("config/messages");
// messageSource.setDefaultEncoding("UTF-8");
// messageSource.setUseCodeAsDefaultMessage(true);
// return messageSource;
// }
}
4) Most importantly... if you're using a WebApplicationInitializer (no web.xml), you've got to register the configuration class that defines the 'messageSource' bean in the root context, not in the dispatcher servlet's context:
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(MvcConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(
dispatcherServlet));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("*.htm");
}
}
In my case, using Spring 4.3.2.RELEASE and java config and a ReloadableResourceBundleMessageSource, I had to define my template engine as a bean otherwise my messages were not getting resolved.
Here's a sample of a working configuration.
AppConfig.java
import java.util.concurrent.TimeUnit;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
#Configuration
#EnableWebMvc
#ComponentScan("myapp")
public class AppConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
private ApplicationContext applicationContext;
private static final boolean CACHE_THYMELEAF_TEMPLATES = false;
private final String UTF8_ENCODING = "UTF-8";
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding(UTF8_ENCODING);
resolver.setCache(CACHE_THYMELEAF_TEMPLATES);
return resolver;
}
#Bean
public TemplateEngine templateEngine() {
//this method must be defined as a bean otherwise i18n messages are not found
//if method defined as private TemplateEngine templateEngine() messages are not found
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setEnableSpringELCompiler(true);
engine.addTemplateResolver(templateResolver());
return engine;
}
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/thymeleaf/");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setSuffix(".html");
resolver.setCacheable(CACHE_THYMELEAF_TEMPLATES);
resolver.setCharacterEncoding(UTF8_ENCODING);
return resolver;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("WEB-INF/i18n/messages");
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setDefaultEncoding(UTF8_ENCODING);
messageSource.setFallbackToSystemLocale(false);
messageSource.setCacheSeconds((int)TimeUnit.HOURS.toSeconds(1));
return messageSource;
}
}
<!-- Application Message Bundle -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="resourcebundles/messages" />
</bean>
You have to configure your messages path as shown above.
Also, check class name.
Is is that if I have to use ResourceBundleMessageSource, I should put
my resource bundles directly under the resources? If i have to keep it
in specified folder only, is there any other way to get this one work?
You can define your messages in your own package, it does not need to be located in the resources folder.
Using Spring 5.2.2.RELEASE versioned components, this is the way I managed to make it work:
The qualified name of the file would be:
/tutproject/src/com/tutproject/app/messages/messages.properties
The bean is defined like this in the my Spring Bean Configuration File (XML):
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename"
value="/com/tutproject/app/messages/messages">
</property>
</bean>
The Java build path includes tutproject/src , this is the part of the location omitted in the XML definition.
Some additional useful information from the ResourceBundleMessageSource class:
The basenames follow {#link java.util.ResourceBundle}
conventions: essentially, * a fully-qualified classpath location. If
it doesn't contain a package qualifier * (such as {#code
org.mypackage}), it will be resolved from the classpath root. * Note
that the JDK's standard ResourceBundle treats dots as package
separators: * This means that "test.theme" is effectively equivalent
to "test/theme".
I have used following configuration and it is working fine
<beans:bean id="messageSource class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="classpath:resourcebundles/ScreenLabelResources" />
</beans:bean>
What worked for me was something really simple.
It was
<property name="basename">
<value>locale\messages</value>
</property>
I changed it to
<property name="basename">
<value>locale/messages</value>
</property>
Just a \ to / change fixed it for me. I am using a MAC.
I have not tried *classpath, that may not have worked for me.
I have used following configuration and it is working fine in my project.
My messages.properties is in below path:
..\WebContent\WEB-INF\resources
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:WEB-INF/resources/messages" />
<property name="useCodeAsDefaultMessage" value="true" />
</bean>
I have used following configuration and it is working fine in my project.
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:configurations/messages" />
<property name="useCodeAsDefaultMessage" value="true" />
</bean>
location: src\main\resources\configurations\messages_en.properties
YAML version for this
spring:
messages:
basename: i18n/validation, i18n/message # for multiple properties just use comma separated values
encoding: UTF-8
You can refer to documentation to see full description.
Also I should mention that the default MessageSource bean is a ResourceBundleMessageSource which is already reading form a classpath so there is no need to use nonation like classpath:i18n/validation.
Directory structure
In Spring Boot 2.2.5 things have slightly changed. Classpath is not needed anymore.
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
I strongly suggest to keep property files out side of project so that we don't need to compile code for every property change.
Below configuration we are using in live project. setting property.location value in application.properties file
#Configuration
public class LocalizationConfiguration {
private static final Logger logger = LoggerFactory.getLogger(LocalizationConfiguration.class);
#Value("${property.location}")
private String propertyLocation;
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.ENGLISH); // change this
return localeResolver;
}
#Bean
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
String messageFolderPath = propertyLocation + "/" + "i18n";
resource.setBasename("file:"+messageFolderPath+"/messages");
resource.setDefaultEncoding("UTF-8");
resource.setCacheSeconds(10);
return resource;
}
#Bean
public LocalValidatorFactoryBean validatorFactoryBean() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}
}

Spring MBeanExporter - giving name to MBean

I'm trying to run a simple application with jmx-exported method. I do it like (spring-context and cglib for "#Configuration" are in classpath):
package com.sopovs.moradanen.jmx;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.stereotype.Component;
#Component
#Configuration
public class SpringJmxTest {
public static void main(String[] args) {
new AnnotationConfigApplicationContext("com.sopovs.moradanen.jmx");
while (true) {
Thread.yield();
}
}
#Bean
public MBeanExporter createJmxExporter() {
return new MBeanExporter();
}
public interface FooBarMBean {
public String hello();
}
#Component
public static class FooBar implements FooBarMBean {
#Override
public String hello() {
return "Hello";
}
}
}
However when I run it I get:javax.management.MalformedObjectNameException: Key properties cannot be empty. I tried to debug and solved it with:
#Component
public static class FooBar implements FooBarMBean, SelfNaming {
#Override
public String hello() {
return "Hello";
}
#Override
public ObjectName getObjectName() throws MalformedObjectNameException {
return new ObjectName("fooBar:name=" + getClass().getName());
}
}
But is there a better way to supply a name for MBean?
You can use the descriptions annotations provided by Spring Context #Managed* :
To do this, you must NOT implements the interface with "MBean" or "MXBean" suffix, neither SelfNaming.
Then, the bean will be detected as a standard spring "managed bean" when MBeanExporter will registerBeanInstance(..), and will be converted to a ModelMBean using all spring annotations, including descriptions of attributes, operations, parameters, etc..
As a requirement, you should declare in your spring context the MBeanExporter with AnnotationJmxAttributeSource, MetadataNamingStrategy, and MetadataMBeanInfoAssembler attributes, which can be simplified like this (as described here):
<bean id="mbeanExporter"
class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter" />
or:
<context:mbean-export />
or, using programmatic approach:
#Configuration
#EnableMBeanExport
public class AppConfig {
}
And your managed bean should look like this :
#Component("myManagedBean")
#ManagedResource(objectName="your.domain.jmx:name=MyMBean",
description="My MBean goal")
public class AnnotationTestBean {
private int age;
#ManagedAttribute(description="The age attribute", currencyTimeLimit=15)
public int getAge() {
return age;
}
#ManagedOperation(description = "Check permissions for the given activity")
#ManagedOperationParameters( {
#ManagedOperationParameter(name = "activity",
description = "The activity to check")
})
public boolean isAllowedTo(final String activity) {
// impl
}
}
Remember to not implements an MBean interface, which would be a StandardMBean, and SelfNaming interface, which would bypass Spring naming management !
You can use KeyNamingStrategy to define all JMX-related properties inside XML configuration without adding any compile-time dependencies to Spring into the source code of your MBean:
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="namingStrategy" ref="namingStrategy"/>
</bean>
<bean id="namingStrategy"
class="org.springframework.jmx.export.naming.KeyNamingStrategy">
<property name="mappings">
<props>
<prop key="someSpringBean">desired.packageName:name=desiredBeanName</prop>
</props>
</property>
</bean>
If you can live with somewhat arbitrary object names, then you can use the IdentityNamingStrategy as a naming strategy for MBeanExporter and minimize the XML configuration event further:
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="namingStrategy" ref="namingStrategy"/>
</bean>
<bean id="namingStrategy"
class="org.springframework.jmx.export.naming.IdentityNamingStrategy"/>
Check spring documentation: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jmx.html section 22.3.2 explains the JDK 5.0 annotations that are available.
Section 22.4 explains mechanisms available for object naming.

Resources