Spring setting WebApplicationContext in ServletContextAware bean - spring

I'm converting existing code to Spring 3 JDBC. I've put it into a class (SpringDB.Users) that implements ServletContextAware. In setServletContext(), the following code doesn't work:
public void setServletContext(ServletContext sc)
{
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
simpleJdbcTemplate = (SimpleJdbcTemplate) wac.getBean("simpleJdbcTemplate");
}
The reason is: exception is java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
However I did register a ContextLoaderListener in web.xml:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
The applicationContext.xml has this:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/mysql"/>
<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg>
<ref bean="dataSource"/>
</constructor-arg>
</bean>
<bean class="SpringDB.Users"/>
which results in getting the call to setServletContext(). The class SpringDB.Users is mostly static stuff. It is never instantiated by the java code.
Apparently, the call to WebApplicationContextUtils.getRequiredWebApplicationContext() is "too early". Because what does work without any trouble is to get the WebApplicationContext at a later time, i.e. when database work really starts - so what I do is to call a private function getSimpleJdbcTemplate() instead of a private variable simpleJdbcTemplate:
static private SimpleJdbcTemplate getSimpleJdbcTemplate ()
{
if (simpleJdbcTemplate == null)
{
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
simpleJdbcTemplate = (SimpleJdbcTemplate) wac.getBean("simpleJdbcTemplate");
}
return simpleJdbcTemplate;
}
Is there any solution so that the variable simpleJdbcTemplate can be initialized within setServletContext() ?
Am I missing something obvious, or just expecting too much?

Why do you need that in the first place?
If your class is a bean, then you can simply inject (with #Inject, #Autowired or xml) the jdbc template:
#Inject
private SimpleJdbcTemplate template;

Related

How call a method in a spring bean on web context initialization

I have a war file that includes the following
Spring Bean
public class DataLoader {
private static Logger log = Logger.getLogger(DataLoader.class.getName());
public void init() {
log.info("DataLoader init called");
}
}
applicationContext.xml
<bean id="dataLoader" class="com.example.DataLoader"
init-method="init" lazy-init="false">
</bean>
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
I want the init method in the DataLoader to be called when I deploy the war file to a web container. However, it does not get called.
I thought that the init method would get called after the bean is initialized because of init-method="init" in the bean configuration, and that the bean would be initialized on deployment because of lazy-init="false".
What am I doing wrong?
Are you sure your bean is being initialized at all? You probably want to make your DataLoader class extend InitializingBean, and rename init to afterPropertiesSet. The more modern way of doing this, however, would be to drop the XML configuration for the bean, and alter you class in this way:
#Component
public class DataLoader {
private static Logger log = Logger.getLogger(DataLoader.class.getName());
#PostConstruct
public void init() {
log.info("DataLoader init called");
}
}

Accessing a Spring Bean from a Servlet

I have a Spring Bean defined in my applicationContext like:
<bean id="spaceReader" class="com.company.SpaceReader">
</bean>
I would like to be able to access this bean in my Application Servlet without having to use:
ApplicationContext context = new ClassPathXmlApplicationContext(CONTEXT_LOCATION);
context.getBean("SpaceReader");
I've tried exporting it using the following:
<bean id="ContextExporter" class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="contextExporterAttributes">
<map>
<entry key="SpaceReaderKey">
<ref local="spaceReader" />
</entry>
</map>
</property>
</bean>
but when i inject it into the Servlet, it returns a Null value. Just wondering if there's something i'm missing when i export the Bean or when i try to access it in the Servlet?
You can inject dependencies using annotations even in servlet (there is a special SpringBeanAutowiringSupport helper class for this pourpose):
public class CustomServlet extends HttpServlet {
#Autowired
private ProductService productService;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// inject productService dependency
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
....
}

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
}
}

SimpleJdbcTemplate not serializable

I use JSF and Spring. The JSF beans must be serializable by design. The JSF beans get injected with Spring beans that handle the db access. Thus these beans must be serializable as well. However simpleJdbcTemplate is not serializable so I get stack traces when restarting the servlet and sessions are lost. If I declare simpleJdbcTemplate to be transient, I don't get stack traces about "not serializable" but then simpleJdbcTemplate is sometimes NULL after a restart, which is even worse.
So what can I do so that the spring beans get injected with a "fresh" simpleJdbcTemplate when the servlet is restarted?
The classes look like this:
#Repository
public class Users implements Serializable
{
#Autowired
private SimpleJdbcTemplate simpleJdbcTemplate;
// ...
}
#ManagedBean
#SessionScoped
public class ThisBean implements Serializable
{
#ManagedProperty(value = "#{users}")
private Users users;
public void setUsers(Users users)
{
this.users = users;
}
// ...
}
Now some config files, just so that you see I didn't miss something standard.
The web.xml has this:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>Listeners.SessionListener</listener-class>
</listener>
The applicationContext.xml has this:
<context:annotation-config />
<context:component-scan base-package="SpringDB"/>
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/mysql"/>
<bean id="simpleJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg>
<ref bean="dataSource"/>
</constructor-arg>
</bean>
faces-config.xml has this:
<application>
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
<application>
<message-bundle>jsf</message-bundle>
</application>
</application>
Why don't you try to pass in the DataSource reference instead of the JdbcTemplate. The common practice is to inject the data source and create the JdbcTemplate within the setter for the data source. You'll find the following example in the spring documentation:
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
You can also use #Autowired on the setDataSource method if you don't want to set up your beans with XML. For further details have a look at the JdbcTemplate best practices section of the excellent spring documentation.
Add scope="prototype" definition for simpleJDBCTemplate bean. Then Spring Framework will create new instance for every servlet instance. More info about bean scopes: http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes

Another question on Spring 3, servlet, #autowired

I think I've read every question and answer on Spring and autowiring a servlet, both here and at springsource.org, and I still can't get it working.
All I want to do is have the datasource automatically set in my servlets. I understand that the container creates the servlet and not Spring.
Here is code from my test servlet:
package mypackage.servlets;
imports go here...
#Service
public class TestServlet extends HttpServlet
{
private JdbcTemplate _jt;
#Autowired
public void setDataSource(DataSource dataSource)
{
_jt = new JdbcTemplate(dataSource);
}
etc etc
In my applicationContext.xml I have:
<context:annotation-config />
<context:component-scan base-package="mypackage.servlets />
<import resource="datasource.xml" />
and in my datasource.xml:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/db" />
If I can't get this working I'll just use WebApplicationContextUtils in the servlet's init method but I'd really like to make this work after all the reading I've been doing.
I'm using Spring 3, Java 1.6.
Thanks,
Paul
You need to replace your Servlets by Spring MVC contollers. Because Spring will not inject anything the classes (servlets) created by someone else then Spring itselfe (except #Configurable).
(To get an very simple example, take a look at the STS Spring Template Project: MVC).
What I wanted to do was get a DataSource reference in my Servlet for free, i.e. not calling a static getDatasource method on some class.
Here's what I learned and how I got it working:
Servlets cannot be configured or autowired by Spring. Servlets are created before Spring's app context is loaded. See issue SPR-7801: https://jira.springsource.org/browse/SPR-7801
What I did was create a DataSource in my applicationContext.xml and export that as a property:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/db" />
<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="myDatasource">
<ref bean="dataSource"/>
</entry>
</map>
</property>
</bean>
In my servlet's init method I read the property:
public void init(ServletConfig config)
{
Object obj = config.getServletContext().getAttribute("myDatasource");
setDataSource((DataSource)obj);
}
public void setDataSource(DataSource datasource)
{
// do something here with datasource, like
// store it or make a JdbcTemplate out of it
}
If I'd been using DAOs instead of hitting the database from the servlets it would have been easy to wire them up for #Autowired by marking them #Configurable, and also be able to use #Transactional and other Spring goodies.

Resources