I have to create different messageSources programmatically and put them in a Bean in order to use the correct one when needed.
The application must have a messageSource for each of our Customers, so i created a Configuration class
#Configuration
public class MessageSourceConfig implements BeanFactoryAware {
private BeanFactory beanFactory;
#Autowired
private ICompanyService service;
private Map<Company, MessageSource> messageSourceMap = new HashMap<Company, MessageSource>();
// default messageSource
#Bean
#Primary
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setCacheSeconds(5);
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
#PostConstruct
public void onPostConstruct() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
Iterable<Company> companies = service.findAll();
for(Company c : companies) {
String beanName= c.getSlug()+"_messageSource";
MessageSource bean = getCompanyMessageSource(c);
configurableBeanFactory.registerSingleton(beanName, bean);
messageSourceMap.put(c, bean);
}
}
private MessageSource getCompanyMessageSource(Company company) {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("classpath:" + company.getSlug() + "/messages");
ms.setUseCodeAsDefaultMessage(true);
ms.setCacheSeconds(5);
ms.setDefaultEncoding("UTF-8");
return ms;
}
public MessageSource companyMessageSource(Company company) {
return messageSourceMap.get(company);
}
In this way we have a default messageSource and one specific messageSource for each Company.
The idea was to put this specific messageSources into a Map and then accessing the correct one from the map when we need it.
The problem is that companyMessageSource should be a bean, but i cannot pass a parameter to the bean, how can i access dynamically the correct source?
I am not entirely sure I understand how you want to use the created beans, but one way to get the registered singletons of MessageSource is to get them programmatically something like this:
#Service
public class CompanyService {
#Autowired
private ApplicationContext applicationContext;
public void useCompanySpecificMessageSource(Company c) {
MessageSource ms = applicationContext.getBean(c.getSlug() + "_messageSource");
log.debug(ms.getMessage("code", null, new Locale("en", "GB"));
}
}
Hope this helps.
Related
I have a message bundle under my resources in src/main the resources with messages are named messages_en_US.properties i.e. and I am getting the message to my JSP.
#Component
public class Messages {
private MessageSource messageSource;
#Autowired
public Messages(MessageSource messageSource) {
this.messageSource = messageSource;
}
private MessageSourceAccessor accessor;
#PostConstruct
private void init() {
accessor = new MessageSourceAccessor(messageSource);
}
public String get(String code) {
return accessor.getMessage(code);
}
}
When I used the code above without any beans I had an exception that informed me there is no code for a message. So I added a BeanHelper class as below.
#Configuration
#ComponentScan("com")
public class BeansHelper {
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
return messageSource;
}
}
When now I am trying to go on localhost:8000/ I am getting an 404 that says there is no message available. What is wrong in this code? My messages are under src/main in the resources. But the JSP of course are in WEB-INF/views/ any ideas guys how to solve the problem? I correctly added message properties file to my Local.
I created a FactoryBean<Properties> as
public final class SystemProperteisFactoryBean implements FactoryBean<Properties> {
private static final String QUERY = "select * from tb_system_properties";
private final NamedParameterJdbcTemplate jdbcTemplate;
public SystemProperteisFactoryBean (DataSource datasource) {
this.jdbcTemplate = new NamedParameterJdbcTemplate (datasource);
}
#Override
public Properties getObject() {
Properties result = new Properties();
jdbcTemplate.query(QUERY,
(ResultSet rs) -> result.setProperty(rs.getString(1), rs.getString(2));
return result;
}
#Override
public Class<?> getObjectType() {
return Properties.class;
}
#Override
public boolean isSingletone() {
return true;
}
}
This class worked fine using Spring with XML config, I get DataSource using a JNDI name, and then created proper properties, and then used propertiesPlaceHoldeConfigurer via XML tag.
Now I want to use the same thing in Spring Boot and Java Config.
When I define a ProprtySourcesPlaceHolderConfigurer as a bean (in a static method in a #Configuration class) Spring tries to create this bean before the datasource.
Is there any way to create the datasource before PRopertySourcesPlaceHolderConfigurer?
Basically you need to take the dependency as a #Bean method parameter this way:
#Configuration
public static class AppConfig {
#Bean
public SystemPropertiesFactoryBean systemProperties(DataSource dataSource) {
return new SystemPropertiesFactoryBean(dataSource);
}
#Bean
public DataSource dataSource() {
// return new data source
}
}
In an spring boot project, is there a way to use injected object inside a #Bean method. In my example following, isdatasourceUse() method able to acccess injected Datasource (either from dev or war profile)
#EnableScheduling
#Configuration
#EnableAspectJAutoProxy
#Profile({ "dev", "war" })
public class AppConfig {
Logger logger = LoggerFactory.getLogger(AppConfig.class);
#Autowired
DBPropertyBean dbPropertyBean;
#Bean(destroyMethod = "")
#Profile("war")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName(dbPropertyBean.getJndiName());
bean.setProxyInterface(DataSource.class);
bean.setLookupOnStartup(false);
bean.afterPropertiesSet();
return (DataSource) bean.getObject();
}
#Bean(destroyMethod = "close")
#Profile("dev")
public DataSource getDataSource() throws Exception {
com.mchange.v2.c3p0.ComboPooledDataSource ds = new com.mchange.v2.c3p0.ComboPooledDataSource();
ds.setUser(dbPropertyBean.getDsUsername());
ds.setPassword(dbPropertyBean.getDsPassword());
ds.setJdbcUrl(dbPropertyBean.getDsJdbcUrl());
ds.setDriverClass(dbPropertyBean.getDsDriverClass());
ds.setMaxPoolSize(dbPropertyBean.getDsMaxPoolSize());
ds.setMinPoolSize(dbPropertyBean.getDsMinPoolSize());
ds.setInitialPoolSize(dbPropertyBean.getDsInitPoolSize());
ds.setAcquireIncrement(dbPropertyBean.getDsAcquireInc());
ds.setAcquireRetryAttempts(dbPropertyBean.getDsAcquireRetryAtt());
ds.setPreferredTestQuery(dbPropertyBean.getPreferredTestQuery());
ds.setIdleConnectionTestPeriod(dbPropertyBean.getIdleConnectionTestPeriod());
return ds;
}
#Bean
public void datasourceUse() {
//How to user datasource here
}
}
Use it like below:
#Autowired
public void datasourceUse(DataSource dataSource) {
System.out.println(dataSource);
}
I am trying to send a custom error for anonymous unauthorized requests through a custom AuthenticationEntryPoint
public class AccessDeniedAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Autowired
private Messages messages;
#Override
public void commence(final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException) throws IOException {
response.setStatus(HttpStatus.SC_UNAUTHORIZED);
response.setContentType(MediaType.APPLICATION_JSON);
response.setCharacterEncoding("UTF-8");
final ObjectMapper mapper = new ObjectMapper();
final ApplicationErrorResponse error = new ApplicationErrorResponse(
Message.PERMISSION_FAILED.toString(),
messages.get(Message.PERMISSION_FAILED.toString()));
if (response.getWriter() != null) {
response.getWriter().write(mapper.writeValueAsString(error));
response.getWriter().flush();
}
}
The Messages bean encapsulates the default MessageSource (messages.properties) and returns the values through a MessageSourceAccessor
#Component
public class Messages {
#Autowired
private MessageSource messageSource;
private MessageSourceAccessor accessor;
#PostConstruct
private void init() {
accessor = new MessageSourceAccessor(messageSource);
}
public String get(String code) {
return accessor.getMessage(code);
}
}
However when an AccessDendiedException is raised and the commence method in the AccessDeniedAuthenticationEntryPoint is called, the injected/autowired Messages object is null.
I just couldn't get Spring to successfully inject Messages in a custom Authentication Entry Point.
I even tried registering a bean of type MessageSource
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
and tried autowiring it in the AuthenticationEntryPoint but it didn't work too.
How can I successfully inject/autowire a MessageSource in a custom AuthenticationEntryPoint?
I have a Tomcat servlet container that has a list of DataSources managed by Tomcat's connection pool. From my Spring application (Spring 3.2.3) I would like to get one of these datasources on runtime, something like:
public class MyService {
#Autowired
private JndiObjectLocator jndiLocator;
public void myMethod(String jndiName) {
DataSource myDataSource = jndiLocator.locate(jndiName);
}
}
Any ideas on how to do that?
You can always do a JNDI lookup in your code, you can use the JndiDataSourceLookup for that and call the getDataSource() method.
public class MyService {
#Autowired
private JndiDataSourceLookup lookup;
public void myMethod(String jndiName) {
DataSource myDataSource = lookup.getDataSourcejndiName);
}
}
Another option would be to make your bean aware of the BeanFactory and retrieve the DataSource from there.
public class MyService {
#Autowired
private BeanFactory factory;
public void myMethod(String jndiName) {
DataSource myDataSource = factory.getBean(jndiName, DataSource.class);
}
}