I am creating a Spring MVC application.
The Controller depends on one of the services [name: TestExecutionOrchestratorService.java] in the Service layer. The TestExecutionOrchestratorService depends on a couple of other services [names: JUnitTestExecutorService, QTPTestExecutorService].The configuration is set up to inject these services in a Map.
The Controller and the TestExecutionOrchestratorService are autowired using annotations. The TestExecutionOrchestratorService and its dependencies are wired using XML configuration.
Dependency Issue I am trying to solve:
The Controller is getting an object of TestExecutionOrchestratorService. The TestExecutionOrchestratorService is getting the Map of dependency services injected [I can tell by the log messages printed out]. I save this Map as an instance variable in the TestExecutionOrchestratorService. However, the instance of the TestExecutionOrchestratorService object in the Controller does not seem to have the Map set during dependency injection. In other words:
Controller---DEPENDS ON--->TestExecutionOrchestratorService---DEPENDS ON--->Map[of JUnitTestExecutorService, QTPTestExecutorService] The Map is empty in the instance of the TestExecutionOrchestratorService set in the Controller. I know from log messages that the Map is being injected during server startup.
The code and XML files are below:
Listing 1 - Controller
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
@Autowired
private TestExecutionOrchestratorService testExecutionOrchestratorService;
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! the client locale is "+ locale.toString());
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
//ISSUE: Call to the service layer. The Map in the service is empty even though dependency injection is happening
//there.
if (testExecutionOrchestratorService != null) {
logger.info("testExecutionOrchestratorService is not null. Executing it...");
testExecutionOrchestratorService.runTests();
}
else {
logger.info("testExecutionOrchestratorService is null. Why Why Why??");
}
return "home";
}
}
Listing 2 - TestExecutionOrchestratorService
/*
* This class is the entry point into test execution. It takes the request to execute tests
* and calls the appropriate test executors.
*/
@Service("testExecutionOrchestratorService")
public class TestExecutionOrchestratorService implements TestResultsReporter {
/* List of executor services */
private Map<String, TestExecutorService> testExecutors = new HashMap<String, TestExecutorService>();
private static final Logger logger = LoggerFactory.getLogger(TestExecutionOrchestratorService.class);
/*
* For Spring's dependency injection - to inject all the test executors.
*/
public void setExecutormap(Map<String, TestExecutorService> exMap) {
if (exMap != null) {
Set<Entry<String, TestExecutorService>> mapEntrySet = exMap.entrySet();
logger.error("TestExecutionOrchestratorService [setExecutorMap]: Got a map of test executors. The entries are:");
for (Entry<String, TestExecutorService> mapE: mapEntrySet) {
logger.error("Key: " + mapE.getKey() + ", Value: " + mapE.getValue().getExecutorName());
}
//ISSUE: testExecutors is showing as null in the "runTests" method that is called by the Controller. Why??
testExecutors.putAll(exMap);
}
else {
logger.error("TestExecutionOrchestratorService [setExecutorMap]: Got a null executors map");
}
}
/* runTests - Calls upon the various executors to run the tests.
* ISSUE: The Controller is calling this method but the Map set in 'setExecutorMap' is not being found. */
public void runTests() {
logger.error("TestExecutionOrchestratorService [runTests]: Entering the method");
/* Create a unique test run ID. This will be the current time stamp. */
String testRunTimestamp = new Timestamp(new Date().getTime()).toString();
logger.error("TestExecutionOrchestratorService [runTests]: Will execute executors with test run ID: " + testRunTimestamp);
/* Call each executor and ask them to run their default tests. */
if ((testExecutors != null) && (!testExecutors.isEmpty())) {
logger.error("TestExecutionOrchestratorService [runTests]: test executors are available. Will execute them.");
Collection<TestExecutorService> teServices = testExecutors.values();
for (TestExecutorService teService: teServices) {
teService.runTests(testRunTimestamp, null, this);
}
}
else {
/* ISSUE: THIS IS WHERE EXECUTION IS ALWAYS COMING */
logger.error("TestExecutionOrchestratorService [runTests]: There are no test executors available.");
}
}
}
Listing 3 - Spring's Web context XML file (root-context.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="..">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- Defining services -->
<!-- The following services are defined here: TestExecutionOrchestratorService,
JUnitTestExecutorService, QTPTestExecutorService -->
<!-- Definition of the JUnitTestExecutorService -->
<bean id="junitexecutor" name="junitexecutor" class="com.testing.autofwk.execution.JUnitTestExecutorService" />
<!-- Definition of the QTPTestExecutorService -->
<bean id="qtpexecutor" name="qtpexecutor" class="com.testing.autofwk.execution.QTPTestExecutorService" />
<!-- Definition of the TestExecutionOrchestratorService -->
<bean id="testexecutororchestrator" name="testexecutororchestrator" class="com.testing.autofwk.execution.TestExecutionOrchestratorService">
<property name="executormap">
<map>
<entry key="junit">
<ref local="junitexecutor"/>
</entry>
<entry key="qtp">
<ref local="qtpexecutor"/>
</entry>
</map>
</property>
</bean>
<context:component-scan base-package="com.testing.autofwk.execution" />
</beans>
Listing 4 - The MVC application dispatcher's context XML file (servlet-context.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="..">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.testing.autofwk" />
</beans:beans>