Convert application context to Java Config Springboot - spring

Looking for some guidance on converting below in existing applicationContext.xml into Java Config for a new Springboot application.
<bean id="InitialHandler" class="package.InitialHandler"
scope="prototype">
<property name="rawResponseTemplate"
ref="rawResponseRestTemplate"/>
<property name="mimeMapper" ref="initialMimeMapper"/>
</bean>
<bean id="rawResponseRestTemplate"
class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="package.CommonDocumentRawMessageConverter"/>
</list>
</property>
</bean>
<bean id="initialMimeMapper"
class="package.ConfigurationBasedMapper">
<property name="configuration" ref="initialMimeConfig"/>
</bean>
<bean id="initialMimeConfig"
class="org.apache.commons.configuration.XMLPropertiesConfiguration"
scope="singleton">
<constructor-arg type="java.net.URL"
value="classpath:FileINeedLoaded.xml"/>
<property name="reloadingStrategy" ref="ReloadingStrategy"/>
</bean>
// snippet of CommonDocumentRawMessageConverter below
import org.springframework.http.converter.HttpMessageConverter;
public class CommonDocumentRawMessageConverter implements
HttpMessageConverter<CommonDocument> {}
This is what i have:
#Bean
public RestTemplate rawResponseRestTemplate() {
RestTemplate rawResponseRestTemplate = new RestTemplate();
rawResponseRestTemplate.setMessageConverters(msgConverters());
return rawResponseRestTemplate;
}
private List<HttpMessageConverter<?>> msgConverters() {
List<HttpMessageConverter<?>> newList = new ArrayList<HttpMessageConverter<?>>();
newList.add(new CommonDocumentRawMessageConverter());
return newList;
}
Service compiles OK but get runtime exception as follows:
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'prop.mime-mapper' to
package.util.Mapper<java.lang.String>:
Property: prop.mime-mapper
Value: propMimeMapper
Origin: "prop.mimeMapper" from property source "Config resource 'class
path resource [application.yml]' via location 'optional:classpath:/'
(document #0)"
Reason: org.springframework.core.convert.ConverterNotFoundException: No
converter found capable of converting from type [java.lang.String] to
type [package.util.Mapper<java.lang.String>]
Action:
Update your application's configuration
Snippet of my config:
#Getter
#Setter
public class propConfig {
private String randomProp;
private Mapper<String> mimeMapper;
private RestTemplate rawResponseTemplate;
Service:
#Slf4j
#Service
public class PropService extends DocumentManagementService {
private final RestTemplate rawResponseTemplate;
private final Mapper<String> mimeMapper;
public PropService(#Autowired PropConfig propConfig) {
this.rawResponseTemplate = propConfig.getRawResponseTemplate();
this.mimeMapper = propConfig.getMimeMapper();
}
It appears that at runtime the properties defined as RestTemplate and Mapper are not getting converted for some reason.
UPDATE ONE
I was able to fix above issue by removing these properties from the application.yaml altogether.
private Mapper<String> mimeMapper;
private RestTemplate rawResponseTemplate;
I realised that these properties were actually 'refs' in the original applicationContext.xml and thus not required in the new application.yaml. I do have new issues however related to this conversion which i will share. I will keep original configs in place in case others have same issue.

I was able to fix above issue by removing these properties from the application.yaml altogether.
private Mapper<String> mimeMapper;
private RestTemplate rawResponseTemplate;
I realised that these properties were actually 'refs' in the original applicationContext.xml and thus not required in the new application.yaml.

Related

Load configuration files dynamically using Spring

We already have a setup in which we are loading files like :
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:${AN_ENV_CONFIGURED_DIR}/project.properties</value>
</list>
</property>
The config files are there in server in a separate location. I want to load the configuration files dynamically if there is a change in it. Now since I already have this setup is there a simple way to reload config files from Spring other than using TimerTask and if its the only way then still its not sufficing the need to loading the file immediately.
You can use ReloadableResourceBundleMessageSource, following is the code snippet.
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>file:${AN_ENV_CONFIGURED_DIR}/project.properties</value>
</list>
</property>
<property name="cacheSeconds" value="1"/>
</bean>
For more info : http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/support/ReloadableResourceBundleMessageSource.html
Use the below bean to reload config.properties every 1 second.
#Component
public class PropertyLoader {
#Autowired
private StandardEnvironment environment;
#Scheduled(fixedRate=1000)
public void reload() throws IOException {
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource<?> resourcePropertySource = propertySources.get("class path resource [config.properties]");
Properties properties = new Properties();
InputStream inputStream = getClass().getResourceAsStream("/config.properties");
properties.load(inputStream);
inputStream.close();
propertySources.replace("class path resource [config.properties]", new PropertiesPropertySource("class path resource [config.properties]", properties));
}
}
Your main config will look something like:
#EnableScheduling
#PropertySource("classpath:/config.properties")
public class HelloWorldConfig {
}
Then access the property any where:
#Autowired
private Environment environment;
environment.get("my.property");

#Value in my spring controller does not work

My controller has
#Value("${myProp}")
private String myProp;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
#RequestMapping(value = "myProp", method = RequestMethod.GET)
public #ResponseBody String getMyProp(){
return "The prop is:" + myProp;
}
my applicationcontext.xml has
<bean id="appConfigProperties" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="classpath:MyApps-local.properties" />
</bean>
I get the following exception:
Caused by: java.lang.IllegalArgumentException: Could not resolve
placeholder 'myProp' in string value "${myProp}"
Note: My properties file MyApps-local.properties is in the classpath and contains
myProp=delightful
any help would be great....
In XML based configuration you need to use PropertyPlaceholderConfigurer bean
<beans xmlns="http://www.springframework.org/schema/beans" . . . >
. . .
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:database.properties" />
</bean>
. . .
</beans>
but you can use values from database.properties in xml configuration only
<beans xmlns="http://www.springframework.org/schema/beans" . . >
. . .
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
. . .
</beans>
If you want to use values from properties files inside #Value annotation for bean's fields, you need to add #Confuguration and #PropertySource annotation to bean class. Like this
#Configuration
#PropertySource("classpath:database.properties")
public class AppConfig {
#Value("${jdbc.url}")
private String url;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
In your configuration you have two PropertySourcesPlaceholderConfigurer. The first one define in the DispatcherServlet using Java #Configuration uses the standard environment to find properties. This means properties from system environment and environment variables as well as an defined #PropertySource.
This is not aware of your MyApps-local.properties file specified in the applicationContext.xml. The second PropertySourcesPlaceholderConfigurer in the xml file is aware of the MyApps-local.properties file but it only post process placeholder in the root application context
bean factory post processors are scoped per application context.
Change you web application context to specify the property source.This will add the properties in the file to your environment
#Configuration
#PropertySource("classpath:MyApps-local.properties")
public class WebConfig{
#Autowired
private Environment env;
#RequestMapping(value = "myProp", method = RequestMethod.GET)
public #ResponseBody String getMyProp(){
return "The prop is:" + env.getProperty("myProp");
}
}
In this case you dont need the PropertySourcesPlacheholder as you can query for he properties from the environment. Then keep your applicationContext.xml as is
It will also work with the PropertySourcesPlaceholder #Bean as it also picks properties from the enviroment. However the abover is clean than below
#Configuration
#PropertySource("classpath:MyApps-local.properties")
public class WebConfig{
#Value("${myProp}")
private String myProp;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
#RequestMapping(value = "myProp", method = RequestMethod.GET)
public #ResponseBody String getMyProp(){
return "The prop is:" + myProp;
}
}
See if this helps
You could go on and use this util tag in your xml configuration
<util:properties id="appConfigProperties" location="location to your properties file" />
<context:property-placeholder properties-ref="appConfigProperties" />
Comes from schema
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
Then in you code where you want your value from property file use it as
#Value("${myPropl}")
private String str;
works for me, let me know if stuck any where :)
Read carefully this http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files
For dev-mode use profile-specific properties

Spring 3 MVC:java.lang.IllegalArgumentException: Property 'dataSource' is required.How to set JdbcTemplate correctly?

I'm new to Spring development.And right now,i'm really facing a problem.Here are the code snippets to make you realize my problem clearly.............
Here is my DAO class:
public class LoginDaoImpl {
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public int checkLoginDetails(LoginVo loginVo){
String sql = "select count(*) from empsctygrp where username=? and password=?";
jdbcTemplate = new JdbcTemplate(dataSource);
int count = jdbcTemplate.queryForObject(sql,new Object[]{loginVo.getUserName(),loginVo.getPassword()},Integer.class);
return count;
}
}
Now here is my Business-Object(BO) class:
public class LoginBo {
LoginDaoImpl loginDaoImpl = new LoginDaoImpl();
public int checkLoginDetails(LoginVo loginVo){
return loginDaoImpl.checkLoginDetails(loginVo);
}
}
Now,here is my dispatcher-servlet xml code:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#117.194.83.9:1521:XE"/>
<property name="username" value="system"/>
<property name="password" value="password1$"/>
</bean>
<bean id="loginDaoImpl" class="com.abhinabyte.dao.LoginDaoImpl">
<property name="dataSource" ref="dataSource" />
</bean>
Now whenever i'm trying to run this on server the following exception is given:
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/A] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: Property 'dataSource' is required] with root cause
java.lang.IllegalArgumentException: Property 'dataSource' is required
Please help me solve this problem.............:(
Try this in LoginBo class:
#Autowired
LoginDaoImpl loginDaoImpl;
instead of
LoginDaoImpl loginDaoImpl = new LoginDaoImpl();
The problem is that you manually instantiate LoginDaoImpl.
I was having the same problem and could not find a comprehensive answer on the web, so I decided to post one here for anyone else, or for future me.
I'm still learning so if you think I have made a mistake below, please feel free to edit.
Summary:
Include <integration:annotation-config/> <context:component-scan base-package="myproject"/> in your servlet to pick up annotations
Configure JUnit tests with #RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("file:WEB-INF/FinanceImportTool-servlet.xml")
Don't autowire dataSource or jdbcTemplate if these fields are already provided by a parent class e.g. StoredProcedure
Don't use new() as this initializes classes outside the applicationContext
Beware of using properties in your constructor which have not yet been set - obvious but embarrassingly easy to do
My original class (now altered):
public class MyDAOImpl extends StoredProcedure implements MyDAO {
private static final String SPROC_NAME = "dbo.MySP";
public MyDAOImpl(DataSource dataSource) {
super(dataSource, SPROC_NAME);
// ...declared parameters...
compile();
}
}
MyProject-servlet.xml file (only relevant bits included):
<!-- Used by Spring to pick up annotations -->
<integration:annotation-config/>
<context:component-scan base-package="myproject"/>
<bean id="MyDAOBean" class="myproject.dao.MyDAOImpl" >
<constructor-arg name="dataSource" ref="myDataSource"/>
</bean>
<!-- properties stored in a separate file -->
<bean id="myDataSource" class="com.microsoft.sqlserver.jdbc.SQLServerDataSource">
<property name="databaseName" value="${myDataSource.dbname}" />
<property name="serverName" value="${myDataSource.svrname}" />
<!-- also loaded portNumber, user, password, selectMethod -->
</bean>
Error: property 'dataSource' is required, or NullPointerException (1)
Other answers say make sure you have passed dataSource as a <property> for your bean in the servlet, etc.
I think #Abhinabyte the OP needed to annotate his setDataSource() method with #Annotation, and use <integration:annotation-config/> <context:component-scan base-package="myproject"/> in his servlet to successfully pass in dataSource as a dependency to LoginDaoImpl.
In my case, I tried adding 'dataSource' as a property and autowiring it. The "dataSource is required" error message became a NullPointerException error.
I realised after far too long that MyDAOImpl extends StoredProcedure.
dataSource was already a property of StoredProcedure. By having a dataSource property for MyDAOImpl, the autowiring was not picking up and setting the dataSource property of StoredProcedure, which left dataSource for StoredProcedure as null.
This was not picked up when I tested the value of MyDAOImpl.dataSource, as of course by now I had added a MyDAOImpl.dataSource field that had been autowired successfully. However the compile() method inherited from StoredProcedure used StoredProcedure.dataSource.
Therefore I didn't need public DataSource dataSource; property in MyDAOImpl class. I just needed to use the StoredProcedure constructor with super(dataSource, sql); in the constructor for MyDAOImpl.
I also didn't need a MyDAOImpl.jdbcTemplate property. It was set automatically by using the StoredProcedure(dataSource, sql) constructor.
Error: NullPointerException (2)
I had been using this constructor:
private static final String SPROC_NAME = "dbo.MySP";
public MyDAOImpl(DataSource dataSource) {
super(dataSource, SPROC_NAME);
}
This caused a NullPointerException because SPROC_NAME had not been initialized before it was used in the constructor (yes I know, rookie error). To solve this, I passed in sql as a constructor-arg in the servlet.
Error: [same error message appeared when I had changed file name]
The applicationContext was referring to the bin/ instances of my beans and classes. I had to delete bin/ and rebuild the project.
My new class:
public class MyDAOImpl extends StoredProcedure implements MyDAO {
#Autowired // Necessary to prevent error 'no default constructor found'
public MyDAOImpl(DataSource dataSource, String sql) {
super(dataSource, sql);
// ...declared parameters...
compile();
}
New MyProject-servlet.xml file (only relevant bits included):
<!-- Used by Spring to pick up annotations -->
<integration:annotation-config/>
<context:component-scan base-package="myproject"/>
<bean id="myDAOBean" class="org.gosh.financeimport.dao.MyDAOImpl" >
<constructor-arg name="dataSource" ref="reDataSource"/>
<constructor-arg name="sql" value="dbo.MySP" />
</bean>
<!-- properties stored in a separate file -->
<bean id="myDataSource" class="com.microsoft.sqlserver.jdbc.SQLServerDataSource">
<property name="databaseName" value="${myDataSource.dbname}" />
<property name="serverName" value="${myDataSource.svrname}" />
<!-- also loaded portNumber, user, password, selectMethod -->
</bean>
Helpful places:
If you can get past the rage, this answer on Spring forums might help too
This answer gives a broad introduction to Spring configuration
This answer has simple but useful suggestions
You should annotate that beans that will suffer IoC. Like
#Bean public class LoginDAOImpl { #Inject DataSource dataSource;......}
You set up in spring context this beans, but, you're not using them.
OBS:
When I use the JDBCTemplate I configure de IoC of JDBC like
<bean id="dataSourcePerfil" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${br.com.dao.jdbc.driver}" />
<property name="url" value="${br.com.dao.jdbc.url}" />
<property name="username" value="${br.com.dao.jdbc.user}" />
<property name="password" value="${br.com.dao.jdbc.pass}" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSourcePerfil" />
</bean>
then.... after at all
#Bean
public class LoginDAOImpl {
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public List<ClienteReport> getClientes() {
return Collections<ClienteReport>. emptyList();
}
}

How to log properties loaded by Spring?

is it possible to simply log all the content of the properties file loaded by spring with <context:property-placeholder /> ?
Thanks
You can set the log level of org.springframework.core.env.PropertySourcesPropertyResolver to "debug". Then, you will be able to see the value of the properties during resolving.
You can do it this way:
<context:property-placeholder properties-ref="myProperties"/>
<bean id="myProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
.. locations
</list>
</property>
</bean>
and add a logging bean similar to one below (here annotation based and with slf4j api):
#Component
public class PropertiesLogger {
private static Logger logger = LoggerFactory.getLogger(PropertiesLogger.class);
#Resource("myProperties")
private Properties props;
#PostConstruct
public void init() {
for (Map.Entry<Object, Object> prop : props.entrySet()) {
logger.debug("{}={}", prop.getKey(), prop.getValue());
}
}
}

Inject a file resource into Spring bean

What is a good way to inject some file resource into Spring bean ?
Now i autowire ServletContext and use like below. Is more elegant way to do that in Spring MVC ?
#Controller
public class SomeController {
#Autowired
private ServletContext servletContext;
#RequestMapping("/texts")
public ModelAndView texts() {
InputStream in = servletContext.getResourceAsStream("/WEB-INF/file.txt");
// ...
}
}
Something like this:
#Controller
public class SomeController {
private Resource resource;
public void setResource(Resource resource) {
this.resource = resource;
}
#RequestMapping("/texts")
public ModelAndView texts() {
InputStream in = resource.getInputStream();
// ...
in.close();
}
}
In your bean definition:
<bean id="..." class="x.y.SomeController">
<property name="resource" value="/WEB-INF/file.txt"/>
</bean>
This will create a ServletContextResource using the /WEB-INF/file.txt path, and inject that into your controller.
Note you can't use component-scanning to detect your controller using this technique, you need an explicit bean definition.
Or just use the #Value annotation.
For single file:
#Value("classpath:conf/about.xml")
private Resource about;
For multiple files:
#Value("classpath*:conf/about.*")
private Resource[] abouts;
What do you intend to use the resource for? In you example you don't do anything with it.
From it's name, however, it looks like you are trying to load internationalisation / localisation messages - for which you can you a MessageSource.
If you define some beans (possibly in a separate messages-context.xml) similar to this:
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>WEB-INF/messages/messages</value>
</list>
</property>
</bean>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="en_GB" />
</bean>
Spring will load your resource bundle when you application starts. You can then autowire the MessageSource into your controller and use it to get localised messages:
#Controller
public class SomeController {
#Autowired
private MessageSource messageSource;
#RequestMapping("/texts")
public ModelAndView texts(Locale locale) {
String localisedMessage = messageSource.getMessage("my.message.key", new Object[]{}, locale)
/* do something with localised message here */
return new ModelAndView("texts");
}
}
NB. adding Locale as a parameter to your controller method will cause Spring to magically wire it in - that's all you need to do.
You can also then access the messages in your resource bundle in your JSPs using:
<spring:message code="my.message.key" />
Which is my preferred way to do it - just seems cleaner.

Resources