Building spring integration route on the fly - spring

I need to build a small reporting application that is producing a report from a database. For now there are just 2 reports that are cronned daily and weekly. OK folks this is what I'm trying to do to make it easily extendable in the future.
1) Scan reports directory for .properties files whose content is like this:
report.name=Weekly Management report
report.datasource=myDataSource
report.bootstrap.sql=SELECT getdate()
report.cron.expression= 0/2 * * * MON-FRI
report.service.activator.class=reporting.qvalent.JDBCReportExtractor
report.recipient.email=konstantin#localhost
2) For each file, create a spring integration route that will do the following:
a) poll database with a bootstrap query according to cron expression
b) invoke activator class that will actually gather all required data from the datasource and maybe enrich Thymeleaf context
c) merge thymeleaf context with the template and email it
What I do now is this:
public class ReportDefinitionLoader implements BeanDefinitionRegistryPostProcessor {
private ConfigurableListableBeanFactory beanFactory;
private void prepareBeansForReport(Resource resource) throws IOException {
try {
Properties props = PropertiesLoaderUtils.loadProperties(resource);
String reportName = props.getProperty(REPORT_NAME_PROPERTY);
String reportDatasource = props.getProperty(REPORT_DATASOURCE_PROPERTY);
String reportCronExpression = props.getProperty(REPORT_CRON_EXPRESSION_PROPERTY);
String reportBootstrapQuery = props.getProperty(REPORT_BOOTSTRAP_QUERY_PROPERTY);
CronTrigger cronTrigger = new CronTrigger(reportCronExpression);
beanFactory.registerSingleton(reportName + CRON_TRIGGER_BEAN_NAME, cronTrigger);
DataSource dataSource = (DataSource) beanFactory.getBean(reportDatasource);
beanFactory.getBean()
beanFactory.autowireBean(dataSource);
QueueChannel channel1 = new QueueChannel();
JdbcPollingChannelAdapter jdbcPollingChannelAdapter = new JdbcPollingChannelAdapter(dataSource, reportBootstrapQuery);
SourcePollingChannelAdapter adapter = new SourcePollingChannelAdapter();
TimerManagerTaskScheduler taskScheduler = new TimerManagerTaskScheduler();
taskScheduler.schedule(new Runnable() {
#Override
public void run() {
}
}, cronTrigger);
adapter.setOutputChannel(channel1);
adapter.setSource(jdbcPollingChannelAdapter);
adapter.setBeanFactory(beanFactory);
adapter.setTaskScheduler(taskScheduler);
adapter.start();
} catch (IOException e) {
LOG.error("Could not load properties from resource: " + resource.getFile().getName(), e);
}
}
}
But the dataSource bean is having unresolved properties as defined in the XML
<bean id="myDataSource" class = "com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.password}" />
Can you please advise how do I get the bean from Spring context for further using it in another class that would also be placed under spring context? At the moment, I'm getting these when it starts up:
2014-09-04 11:15:12,545 [WARN] - Could not load driverClass ${jdbc.driver}
java.lang.ClassNotFoundException: ${jdbc.driver}
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:190)
at com.mchange.v2.c3p0.DriverManagerDataSource.ensureDriverLoaded(DriverManagerDataSource.java:100)
at com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:132)
When I'm using XML configuration, data source properties are resolved OK, so this problem is not because of misconfigured PropertyPlaceholderConfigurer.

I implemented wrong interface - InitializingBean seems to be the right one.
Now I'm creating bean definitions inside afterPropertiesSet() callback. Rubber duck debugging does work! Thanks everyone.

Related

Grails initialization

In my Grails app, I need access to configuration exposed by a Java class similar to the below
public class Config {
private Properties properties = new Properties();
static load(String path) {
File configFile = new File(path);
FileReader fileReader = new FileReader(configFile);
properties.load(fileReader);
}
String getProperty(String name) {
properties.getProperty(name);
}
}
I trigger the initialisation of this class in the first line of Bootstrap.groovy by calling Config.load("/conf.properties"). However, the initialization of various Spring beans needs properties that are exposed by Config, but by the time Bootstrap.groovy is executed, Spring initialization has already completed.
So I need to find a way to call Config.load() before construction of the Spring beans, is this possible? I guess there might be an event handler available in /script/_Events.groovy that I could invoke it from, but I'm not sure which handlers are available.
Unfortunately, changing the source code of Config.java isn't an option, and neither is eliminating my usage of this class.
You could try declaring a suitable bean in web-app/WEB-INF/applicationContext.xml, which is the definition of the root web application context as opposed to the GrailsApplication's internal context.
<bean id="initConfig" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="com.example.Config" />
<property name="targetMethod" value="load" />
<property name="arguments">
<list><value>/conf.properties</value></list>
</property>
</bean>
and modify the grailsApplication bean to depend on that:
<bean id="grailsApplication" depends-on="initConfig" class="...">

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 execute SQL script only once at startup in Spring?

I have a web application based on Spring JDBC and Jersey RESTful web service. I'm using the following Spring JDBC template class to initiate the dataSource and execute an SQL script (update_condition_table.sql):
public class CustomerJDBCTemplate implements CustomerDAO {
private DataSource dataSource;
private JdbcTemplate jdbcTemplateObject;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
Resource rc = new ClassPathResource("update_condition_table.sql");
JdbcTestUtils.executeSqlScript(jdbcTemplateObject, rc, false);
}
// ......other methods
}
The bean configuration file is beans.xml:
<!-- Initialization for data source -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/customer" />
<property name="username" value="root" />
<property name="password" value="mypassword" />
</bean>
<!-- Definition for customerJDBCTemplate bean -->
<bean id="customerJDBCTemplate" class="com.example.db.CustomerJDBCTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
The Jersey controller class contains the instantiation of class CustomerJDBCTemplate and serves as the REST web service:
#Path("/customer")
public class CustomerService {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
CustomerJDBCTemplate dbController = (CustomerJDBCTemplate) context.getBean("customerJDBCTemplate");
// ... some GET/POST methods
}
When I launched my web app by entering the index URL in the browser, the SQL script gets executed by the customerJDBCTemplate bean. However, when I clicked to navigate to other pages, it crashed and reported that the SQL script cannot be executed again. So obviously the SQL script was executed again after initialization of dataSource and initial launch of the index web page. How to avoid this by just running the SQL script only once upon initial startup of the web app?
Looks like I need to move the bean instantiate code out of CustomerService class, but where should I put that code?
I figured it out that I should set the bean application context to be static within CustomerService class and do it in the static initialization block as follows:
#Path("/customer")
public class CustomerService {
private static ApplicationContext context;
private static CustomerJDBCTemplate dbController;
static {
context = new ClassPathXmlApplicationContext("beans.xml");
dbController = (CustomerJDBCTemplate) context.getBean("customerJDBCTemplate");
}
//... other methods
}
I guess the reason is Jersey creates a different instance of CustomerService for each HTTP session (correct me if I'm wrong). So if I set the bean context as instance variable, it will do the initialization for every HTTP request.
Have your CustomerJDBCTemplate implement InitializingBean. afterPropertiesSet will get called once, right after all properties have been set by Spring's BeanFactory.
For example:
public class CustomerJDBCTemplate implements CustomerDAO, InitializingBean {
...
// ......other methods
public void afterPropertiesSet() throws Exception {
//do your initializing, or call your initializing methods
}
}

Spring Transactions not rolling back on Exception (Oracle JNDI datasource)

I am using annotation based transactions in a Spring MVC 3.1 project, and my transactions are not being rolled back when an exception is thrown.
Here is my Service code
#Service
public class ImportService {
#Autowired
ImportMapper importMapper;
#Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED, rollbackFor=Throwable.class)
public void processImport() throws ServiceException, DatabaseException {
iImport import = new Import();
createImport(import);
throw new ServiceException("");
}
#Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED, rollbackFor=Throwable.class)
private void createImport(Import import) throws DatabaseException {
try {
importMapper.createImport(eventImport);
} catch (Exception e) {
throw new DatabaseException(e);
}
}
So, hopefully, the createImport method should be rolled back after the exception is thrown. But unfortunately it's not.
I am defining my datasource in the server context.xml
<Resource name="datasource.import" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="user" password="password" driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:#INFO" />
And I'm looking it up with JNDI:
<jee:jndi-lookup id="dataSource" jndi-name="datasource.import"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven />
I'm using an Oracle database, and the JDBC spec says that auto commit is true by default. I thought that if I set it to false explicitly that would help, but I can't figure out how to do that.
Is there any way to get rollbacks working, while looking up your Oracle datasource by JNDI.
Please be aware that Spring's transaction management rolls-back transactions only for unchecked exceptions (RuntimeException) by default. If you wish to perform rollback also on checked exceptions, you need to define that.
When using annotations as attribute source, you need to provide rollbackFor attribute with the list of exception classes, which should cause a transaction to be rolled-back (quote from the JavaDoc):
/**
* Defines zero (0) or more exception {#link Class classes}, which must be a
* subclass of {#link Throwable}, indicating which exception types must cause
* a transaction rollback.
* <p>This is the preferred way to construct a rollback rule, matching the
* exception class and subclasses.
* <p>Similar to {#link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}
*/
Class<? extends Throwable>[] rollbackFor() default {};
It is said that, if a #transational method is invoked by another #transational method, the first one will not work. You may try to remove the first #transational and have a try.

Autowiring in Spring 3 MDP

Firstly I've checked some of the possible answers that come up when posting a new question and none that I have come across deals with my issue.
I have a Spring MDP which works nicely i.e. can receive messages. The problem is when I try to autowire a dependency, the autowiring doesn't seem to work. I'm using Netbeans and Glassfish 3.1.2 so I'm able to step through the code and can confirm that the dependencies are null. Autowiring in other parts of the application are working fine. The MDP is picked up in the component-scan.
I used the example from springsource to create my MDP:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/jms.html
And I've autowired the dependencies by setter methods.
I cannot figure out why this won't work. I've checked around and I don't think anyone else has had this issue.
Any ideas, pointers in the right direction, examples I can reference will be much appreciated.
Thanks.
KSS
MDP Class:
public class ExampleListener implements MessageListener {
private Transformer transformer;
private MurexService murexService;
#Autowired
public void setTransformer(Transformer transformer) {
this.transformer = transformer;
}
#Autowired
public void setMurexService(MurexService murexService) {
this.murexService = murexService;
}
#Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
}
}
ApplicationContext:
<jee:jndi-lookup id="connectionFactory" jndi-name="jms/QueueConnectionFactory" />
<jee:jndi-lookup id="testQueueOne" jndi-name="jms/ITFS_RECEIVE" />
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="com.scm.service.ExampleListener" />
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="testQueueOne"/>
<property name="messageListener" ref="messageListener" />
</bean>
An AutowiredAnnotationBeanPostProcessor needs to be registered for wiring in the #Autowired fields. The javadoc has more details. See here for the solution to a similar issue.
Essentially adding this should get the autowiring to work:
<context:annotation-config/>

Resources