RayAdverb
asked on
Java / Struts 1.3 / Spring Core Dependency Injection problems and inconsistencies.
Hi everyone.
I have a problem in a web application I'm building. I'm pretty good at programming but trying to learn some new methods and technology. I'm using the following technologies:
So I built the entire application without dependency injection. Just the standard use of the new keyword. And it works. Then when I try to switch it over to use dependency injection, it all falls apart. So far I have focused on getting it working on one Struts Action, called "AddUserAction". AddUserAction takes form data from a JSP page. Then it calls a delegate class, AddUserDelegate, which in turn does some stuff and then calls the DAO class to handle insertion into the database.
I have tried wiring beans through XML and autowiring. Nothing works. What seems to happen is that the beans are injected into the setter (public void setAddUserDelegate(AddUser DelegateIn terface addUserDelegate) and the SETTER is aware of them, but the setter isn't communicating with the Struts Action class. Consequently, when I try to invoke the delegate's insertUserInDao() method, it throws a NullPointerException (which I currently have embedded in a try-catch because I got tired of sifting through the stack trace to find my printed lines).
Here is the relevant code, plus two XML configuration files.
AddUserAction (Struts Action Class)
WEB-INF/spring-beans.xml (bean definition file)
struts-config.xml
When I run this web app and try to submit a new user through my form, here is the output on the console:
For a while, I was getting InstantiationExceptions, and I think that's because I had forgotten to load the Context. But I did that through struts-config.xml. I'm pretty sure the context is being loaded, as evidenced by the first line I bolded above.
The second thing I bolded (the last line) is definitely what's causing the NullPointer Exceptions. It really should be something like DELEGATE: com.eintern.ems.business.A ddUserDele gate@12c81 bd4 (as on the earlier line called from the setter).
Please help. I feel like I'm missing some detail. I have scoured the Internet for answers but found nothing. I swear I am following the instructions for autowiring to the letter and it's not working.
I have a problem in a web application I'm building. I'm pretty good at programming but trying to learn some new methods and technology. I'm using the following technologies:
Java Programming language
Struts 1.3
Spring Core
So I built the entire application without dependency injection. Just the standard use of the new keyword. And it works. Then when I try to switch it over to use dependency injection, it all falls apart. So far I have focused on getting it working on one Struts Action, called "AddUserAction". AddUserAction takes form data from a JSP page. Then it calls a delegate class, AddUserDelegate, which in turn does some stuff and then calls the DAO class to handle insertion into the database.
I have tried wiring beans through XML and autowiring. Nothing works. What seems to happen is that the beans are injected into the setter (public void setAddUserDelegate(AddUser
Here is the relevant code, plus two XML configuration files.
AddUserAction (Struts Action Class)
package com.eintern.ems.actions;
import javax.servlet.http.*;
import org.apache.log4j.Logger;
import org.apache.struts.action.*;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.*;
import org.springframework.context.*;
import org.springframework.context.support.*;
import org.springframework.stereotype.*;
import com.eintern.ems.model.*;
import com.eintern.ems.business.*;
@Component
public class AddUserAction extends Action implements ActionInterface
{
private static final Logger logger = Logger.getRootLogger();
public AddUserDelegateInterface AddUserDelegate;
@Autowired
public void setAddUserDelegate(AddUserDelegateInterface addUserDelegate)
{
AddUserDelegate = addUserDelegate;
logger.info("DOES THIS EVEN READ?");
logger.info("DELEGATE:\t" +addUserDelegate);
AddUserDelegate.PrintALine();
}
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws Exception
{
logger.info("IN THE EXECUTE:\t" +this.AddUserDelegate);
EmployeeBean bean = (EmployeeBean)form;
logger.debug("*****************************************");
logger.debug("ADD USER ACTION EXECUTE METHOD");
logger.debug("*****************************************");
EmployeeBean addedUser;
try
{
addedUser = AddUserDelegate.insertUserInDAO(bean);
}
catch(NullPointerException e)
{
addedUser = null;
}
if(addedUser == null)
return mapping.findForward("failure");
else
{
request.getSession().setAttribute("newEmployee", addedUser);
return mapping.findForward("addSuccessful");
}
}
}
WEB-INF/spring-beans.xml (bean definition file)
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<context:annotation-config/>
<context:component-scan
base-package=
"com.eintern.ems.actions,
com.eintern.ems.ajax,
com.eintern.ems.business,
com.eintern.ems.dao,
com.eintern.ems.model"
/>
<bean id="AddUserDelegateBean"
class="com.eintern.ems.business.AddUserDelegate" scope="singleton" >
</bean>
<bean id="AddUserActionBean"
class="com.eintern.ems.actions.AddUserAction" scope="singleton">
</bean>
</beans>
struts-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
"http://struts.apache.org/dtds/struts-config_1_3.dtd">
<struts-config>
<form-beans>
<form-bean
name="employeeBean"
type="com.eintern.ems.model.EmployeeBean">
</form-bean>
<form-bean
name="roleBean"
type="com.eintern.ems.model.RoleBean">
</form-bean>
<form-bean
name="recordsManagementBean"
type="org.apache.struts.action.DynaActionForm">
<form-property name="selectedRadioButton" type="java.lang.String" />
</form-bean>
</form-beans>
<action-mappings>
<action
path="/pages/pseudologin"
type="com.eintern.ems.actions.RecordsRetrievalAction"
name="roleBean"
scope="session"
validate="false"
input="errors.jsp"
>
<forward name="success" path="/pages/ems_portal.jsp" redirect="true"/>
<forward name="failure" path="/pages/error.jsp" redirect="true" />
</action>
<action
path="/pages/modifyAction"
name="recordsManagementBean"
type="com.eintern.ems.actions.RecordsManagementAction"
scope="request"
validate="false"
input="errors.jsp"
>
<forward name="deleted" path="/pages/delete_success.jsp" redirect="true"/>
<forward name="doUpdate" path="/pages/update_user.jsp" redirect="true"/>
<forward name="failure" path="/pages/error.jsp" redirect="true" />
</action>
<action
path="/pages/updateEmployee"
name="employeeBean"
type="com.eintern.ems.actions.RecordsUpdateAction"
scope="request"
validate="false"
input="errors.jsp"
>
<forward name="updated" path="/pages/update_success.jsp" redirect="true" />
<forward name="failure" path="/pages/error.jsp" redirect="true" />
</action>
<action
path="/pages/addUserAction"
name="employeeBean"
type="com.eintern.ems.actions.AddUserAction"
scope="request"
validate="false"
input="/pages/add_user.jsp"
>
<forward name="addSuccessful" path="/pages/add_success.jsp" redirect="true"/>
<forward name="failure" path="/pages/error.jsp" redirect="true" />
</action>
</action-mappings>
<!-- Message Resources Definitions -->
<message-resources parameter="MessageResources" />
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/spring-beans.xml"/>
</plug-in>
</struts-config>
When I run this web app and try to submit a new user through my form, here is the output on the console:
INFO - Closing WebApplicationContext for namespace 'action-servlet': startup date [Tue Feb 02 08:51:00 EST 2016]; root of context hierarchy
INFO - Destroying singletons in org.springframework.beans.factory.su pport.Defa ultListabl eBeanFacto ry@2962586 1: defining beans [org.springframework.conte xt.annotat ion.intern alConfigur ationAnnot ationProce ssor,org.s pringframe work.conte xt.annotat ion.intern alAutowire dAnnotatio nProcessor ,org.sprin gframework .context.a nnotation. internalRe quiredAnno tationProc essor,org. springfram ework.cont ext.annota tion.inter nalCommonA nnotationP rocessor,o rg.springf ramework.c ontext.ann otation.in ternalPers istenceAnn otationPro cessor,add UserAction ,AddUserDe legateBean ,AddUserAc tionBean,o rg.springf ramework.c ontext.ann otation.Co nfiguratio nClassPost Processor. importAwar eProcessor ]; root of factory hierarchy
INFO - Loading chain catalog from zip:C:/Users/mstabosz/.m2/repository /org/apach e/struts/s truts-core /1.3.10/st ruts-core- 1.3.10.jar !/org/apac he/struts/ chain/chai n-config.x ml
INFO - ContextLoaderPlugIn for Struts ActionServlet 'action, module '': initialization started
INFO - Refreshing WebApplicationContext for namespace 'action-servlet': startup date [Tue Feb 02 08:51:32 EST 2016]; root of context hierarchy
INFO - Loading XML bean definitions from ServletContext resource [/WEB-INF/spring-beans.xml]
INFO - JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning
INFO - JSR-330 'javax.inject.Named' annotation found and supported for component scanning
INFO - JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
INFO - Pre-instantiating singletons in org.springframework.beans.factory.su pport.Defa ultListabl eBeanFacto ry@2a96a55 3: defining beans [org.springframework.conte xt.annotat ion.intern alConfigur ationAnnot ationProce ssor,org.s pringframe work.conte xt.annotat ion.intern alAutowire dAnnotatio nProcessor ,org.sprin gframework .context.a nnotation. internalRe quiredAnno tationProc essor,org. springfram ework.cont ext.annota tion.inter nalCommonA nnotationP rocessor,o rg.springf ramework.c ontext.ann otation.in ternalPers istenceAnn otationPro cessor,add UserAction ,AddUserDe legateBean ,AddUserAc tionBean,o rg.springf ramework.c ontext.ann otation.Co nfiguratio nClassPost Processor. importAwar eProcessor ]; root of factory hierarchy
INFO - DOES THIS EVEN READ?
INFO - DELEGATE: com.eintern.ems.business.AddUserDele gate@12c81 bd4
INFO - You have reached the ADD USER DELEGATE CLASS
INFO - DOES THIS EVEN READ?
INFO - DELEGATE: com.eintern.ems.business.AddUserDele gate@12c81 bd4
INFO - You have reached the ADD USER DELEGATE CLASS
INFO - Using context class 'org.springframework.web.context.sup port.XmlWe bApplicati onContext' for servlet 'action'
INFO - ContextLoaderPlugIn for Struts ActionServlet 'action', module '': initialization completed in 1046 ms
INFO - Initializing composable request processor for module prefix ''
INFO - Initialize action of type: com.eintern.ems.actions.AddUserActio n
INFO - IN THE EXECUTE: null
For a while, I was getting InstantiationExceptions, and I think that's because I had forgotten to load the Context. But I did that through struts-config.xml. I'm pretty sure the context is being loaded, as evidenced by the first line I bolded above.
The second thing I bolded (the last line) is definitely what's causing the NullPointer Exceptions. It really should be something like DELEGATE: com.eintern.ems.business.A
Please help. I feel like I'm missing some detail. I have scoured the Internet for answers but found nothing. I swear I am following the instructions for autowiring to the letter and it's not working.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Great. Thank for updates with detailed answer.
I think by just doing that, it caused the beans to be injected into the AddUserAction class itself, which is why the setter method was finding them. However, the execute method, which Struts is using to do the actual work, still has no idea what those values are.
To explain a little more about what was happening, it's nothing about the beans being injected into a "class", the setter and field that you are storing the bean in are not a "static" so they definitely need an object to work.
What was happening is that Struts was creating an "object" of your AddUserAction class and Spring was creating another object of your AddUserAction class, but these two object are different, separate entities. Spring then injected the delegate into its object so then you see the log messages coming from the setter method. But the Struts version of the object is still untouched. When Struts then tries to execute the action, it calls the exeute() method on the object that IT created, which hasn't had the setter called and so still has null for the delegate and you get your NullPointerException.
By changing the "type" to the DelegatingActionProxy, Struts now creates an object of THAT class, and then when Struts calls .execute() on that object, internally it goes and looks up Springs context to find the bean with the right name and then calls .execute() on that. Now, that bean being the one Spring created, it has had the delegate set correctly.
Just an aside, you probably shouldn't be defining beans in the XML and have the @Component annotation on the Action classes when you have component-scanning defined in the XML. I believe you would actually be creating 2 objects of each Action class. Which won't necessarily break anything but it is wasting time/resources creating extra object that are never used, and potentially making things harder to debug (eg. you may be wondering why your setter would get called twice).
Try to either remove that bean definition from the XML and change the annotation to... @Component("/pages/login")
or...
Take the @Component annotation off or remove the .actions package from the component-scanning and leave the bean definition in the XML.
And along those lines, you may need to check your AddUserDelegate class. If it also has the @Component annotation, that may mean that one is getting instantiated via the component-scanning and one via the XML definition. You may want to just make it consistent across all your bean definitions, ie. either all in the XML or all via annotations and component-scanning.
ASKER
It was the only solution offered and it was what worked for me after I figured it out on my own. I wanted to provide details instead of a useless "I figured it out, nevermind" style post.
ASKER
Hi mccarl. Thanks for the feedback.
You're right, having the XML notation and @Component annotations was redundant and caused some kinds of exceptions. I think my original code was an earlier draft and I got rid of the XML stuff and went full fledged annotations on the non-struts classes. I still have XML for the Struts Action classes.
You're right, having the XML notation and @Component annotations was redundant and caused some kinds of exceptions. I think my original code was an earlier draft and I got rid of the XML stuff and went full fledged annotations on the non-struts classes. I still have XML for the Struts Action classes.
Can you post entire log file ?