Link to home
Start Free TrialLog in
Avatar of RayAdverb
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:

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(AddUserDelegateInterface 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)
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");
			}
			
		}

	

}

Open in new window


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>

Open in new window


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>

Open in new window


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.support.DefaultListableBeanFactory@29625861: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,addUserAction,AddUserDelegateBean,AddUserActionBean,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
INFO - Loading chain catalog from zip:C:/Users/mstabosz/.m2/repository/org/apache/struts/struts-core/1.3.10/struts-core-1.3.10.jar!/org/apache/struts/chain/chain-config.xml
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.support.DefaultListableBeanFactory@2a96a553: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,addUserAction,AddUserDelegateBean,AddUserActionBean,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
INFO - DOES THIS EVEN READ?
INFO - DELEGATE:      com.eintern.ems.business.AddUserDelegate@12c81bd4
INFO - You have reached the ADD USER DELEGATE CLASS
INFO - DOES THIS EVEN READ?
INFO - DELEGATE:      com.eintern.ems.business.AddUserDelegate@12c81bd4
INFO - You have reached the ADD USER DELEGATE CLASS
INFO - Using context class 'org.springframework.web.context.support.XmlWebApplicationContext' 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.AddUserAction
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.AddUserDelegate@12c81bd4 (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.
Avatar of Am P
Am P
Flag of India image

Looks like AddUserDelegate is not injected in AddUserAction which is causing a null reference and you are getting NULL.

Can you post entire log file ?
ASKER CERTIFIED SOLUTION
Avatar of RayAdverb
RayAdverb

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
Avatar of RayAdverb
RayAdverb

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