I'm writing a little web-interface for a database I created. The database access is done through Hibernate, but it also uses Spring for injecting instances of DAO's (which in turn have a SessionFactory injected). I test the web-interface on an embedded Jetty server. Everything works fine when used independently: I can get stuff out of the database from the database project, I can run a webpage using Wicket on the Jetty server, I even managed to get Spring working for the WicketTester, which was a bit of a pain in the ass. But now when I try to get something out of the database (by clicking some button on the webpage), I get an error saying:
java.lang.NullPointerException
at nl.ru.cmbi.pdbeter.core.controller.DAO.GenericDAO.getCurrentSession(GenericDAO.java:37)
Somehow the sessionFactory that should have been injected in the DAO wasn't injected. I defined the DAO bean in applicationContext.xml of the Wicket module, but I assumed that as soon as it would see the #Autowired of the SessionFactory it would look in the spring.xml file of thát project, but apparently it doesn't. I'll put some code below to show the individual parts at work.
The WicketApplication:
#Service
public class WicketApplication extends WebApplication {
#SpringBean
private IPDBEntryDAO pdbEntryDAO;
public IPDBEntryDAO getPdbEntryDAO() {
return pdbEntryDAO;
}
public void setPdbEntryDAO(IPDBEntryDAO pdbEntryDAO) {
this.pdbEntryDAO = pdbEntryDAO;
}
/**
* #see org.apache.wicket.Application#getHomePage()
*/
#Override
public Class<HomePage> getHomePage() {
return HomePage.class;
}
/**
* #see org.apache.wicket.Application#init()
*/
#Override
public void init() {
super.init();
new ClassPathXmlApplicationContext("applicationContext.xml").getAutowireCapableBeanFactory().autowireBean(this);
}
public void setupInjector() {
getComponentInstantiationListeners().add(new SpringComponentInjector(this));
}
}
The class which starts the embedded Jetty server (copied from the Wicket quickstart):
public class Start {
public static void main(String[] args) throws Exception {
int timeout = (int) Duration.ONE_HOUR.getMilliseconds();
Server server = new Server();
SocketConnector connector = new SocketConnector();
// Set some timeout options to make debugging easier.
connector.setMaxIdleTime(timeout);
connector.setSoLingerTime(-1);
connector.setPort(8080);
server.addConnector(connector);
// check if a keystore for a SSL certificate is available, and
// if so, start a SSL connector on port 8443. By default, the
// quickstart comes with a Apache Wicket Quickstart Certificate
// that expires about half way september 2021. Do not use this
// certificate anywhere important as the passwords are available
// in the source.
Resource keystore = Resource.newClassPathResource("/keystore");
if (keystore != null && keystore.exists()) {
connector.setConfidentialPort(8443);
SslContextFactory factory = new SslContextFactory();
factory.setKeyStoreResource(keystore);
factory.setKeyStorePassword("wicket");
factory.setTrustStoreResource(keystore);
factory.setKeyManagerPassword("wicket");
SslSocketConnector sslConnector = new SslSocketConnector(factory);
sslConnector.setMaxIdleTime(timeout);
sslConnector.setPort(8443);
sslConnector.setAcceptors(4);
server.addConnector(sslConnector);
System.out.println("SSL access to the quickstart has been enabled on port 8443");
System.out.println("You can access the application using SSL on https://localhost:8443");
System.out.println();
}
WebAppContext bb = new WebAppContext();
bb.setServer(server);
bb.setContextPath("/");
bb.setWar("src/main/webapp");
// START JMX SERVER
// MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
// MBeanContainer mBeanContainer = new MBeanContainer(mBeanServer);
// server.getContainer().addEventListener(mBeanContainer);
// mBeanContainer.start();
server.setHandler(bb);
try {
System.out.println(">>> STARTING EMBEDDED JETTY SERVER, PRESS ANY KEY TO STOP");
server.start();
System.in.read();
System.out.println(">>> STOPPING EMBEDDED JETTY SERVER");
server.stop();
server.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
The web.xml file:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>web-interface</display-name>
<!--
There are three means to configure Wickets configuration mode and they
are tested in the order given.
1) A system property: -Dwicket.configuration
2) servlet specific <init-param>
3) context specific <context-param>
The value might be either "development" (reloading when templates change) or
"deployment". If no configuration is found, "development" is the default. -->
<filter>
<filter-name>wicket.web-interface</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>nl.ru.cmbi.pdbeter.WicketApplication</param-value>
</init-param>
<init-param>
<param-name>applicationFactoryClassName</param-name>
<param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value>
</init-param>
<init-param>
<param-name>applicationBean</param-name>
<param-value>wicketApplication</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>wicket.web-interface</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- The SpringWebApplicationFactory will need access to a Spring Application context, configured like this... -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
The applicationContext.xml file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<!--
<context:component-scan base-package="nl.ru.cmbi.pdbeter" />
-->
<!-- setup wicket application -->
<bean id="wicketApplication" class="nl.ru.cmbi.pdbeter.WicketApplication">
<property name="pdbEntryDAO" ref="pdbEntryDAO"/>
</bean>
<bean id="pdbEntryDAO" class="nl.ru.cmbi.pdbeter.core.controller.DAO.PDBEntryDAO" />
</beans>
Part of the DAO class from the database module:
public class GenericDAO<I> implements IGenericDAO<I> {
private Class<I> persistentClass;
#Autowired
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
public GenericDAO() {
persistentClass = (Class<I>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
protected Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
The spring.xml file used by the database module:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<context:component-scan base-package="nl.ru.cmbi.pdbeter" />
<!-- Transaction Manager -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven />
<!-- Session Factory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="hibernate.cfg.xml" />
<property name="packagesToScan" value="nl.ru.cmbi.pdbeter.core.model.domain" />
</bean>
</beans>
Sorry for posting so much code, but I think I might let important stuff out if I don't.
Does anyone know how to make sure everything gets injected, both for the Wicket module as for the database module?
Related
I have a project in grails 3, that has a project spring dependency, in the spring project, xml beans are configured. How should import the bens in grails architecture?
build.gradle
dependencies {
compile (project(':spring-project')) { transitive = false }
}
settings.gradle
includeFlat 'spring-project'
I tried the following ways:
in the resources.groovy load the beans:
beans = {
importBeans('path/to/beans-definition.xml')
}
in the resources.xml:
<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:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<import resource="path/to/beans-definition.xml" />
</beans>
Besides that, in the spring project, beans using java annotation are configured. The beans is not working in grails app, even by setting the spring project packages in the conponent scan.
#ComponentScan(basePackages = ["package.spring.project.beans"])
class Application extends GrailsAutoConfiguration {
static void main(String[] args) {
GrailsApp.run(Application, args)
}
}
But, nothing works. Any help would be appreciated..
Beans can also be configured using a grails-app/conf/spring/resources.xml. In earlier versions of Grails this file was automatically generated for you by the run-app script, but the DSL in resources.groovy is the preferred approach now so it isn’t automatically generated now. But it is still supported - you just need to create it yourself.
<bean id="myBean" class="my.company.MyBeanImpl">
<property name="someProperty" value="42" />
<property name="otherProperty" value="blue" />
further check this link
I have a context as follows
<?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:int="http://www.springframework.org/schema/integration"
xmlns:int-amqp="http://www.springframework.org/schema/integration/amqp"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/amqp
http://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<int:gateway id="fooGateway" service-interface="com.foo.FooGateway" />
</beans>
My gateway interface is as follows:
public interface FooGateway
{
public void foo(String foo);
}
This context is imported into another context but the fooGateway bean is never created by GatewayProxyFactoryBean.
I've set a breakpoint in GatewayProxyFactoryBean and I can see that it is not creating a proxy for this interface.
Turn on DEBUG logging for org.springframework; the bean creation process emits lots of information including when it reads particular configuration files.
im using spring for my java web app. the site has got bigger and i would like to set some configurations.
i have been researching and came across things like document builder factory, replacing spring xml with java config and others. i dunno where to start.
im thinking of implementing the configurations in xml (WEB/newConfig.xml) and have it read by the java beans. basically i wanna input my cofiguration values into xml and have it load by a java bean so that i can use it in controllers and jstl.
im just giving some examples here. for example xml configurations:
<property name="numberOfCars" value="3" />
<property name="webSiteName" value="New Spring Web App" />
....
and i read it in my java class:
class Config {
public getNumberOfCars() {
return numOfCars;
}
public getWebSiteName() {
return webSiteName;
}
}
where should i start and what online materials can i read?
==============================
update
here is what i have created.
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:property-placeholder location="/WEB-INF/your_prop_file.properties" />
<bean id="ConfigMgr" class="org.domain.class.ConfigMgr">
<property name="username" value="${username}">
</bean>
</beans>
you_prop_file.properties
username=hello world name
ConfigMgr.java
public class ConfigMgr {
private String username;
...getter
...setter
}
in my controller, here is what i did:
ConfigMgr config = new ConfigMgr();
sysout.out.println(config.getUsername());
i am getting null and i am sure im missing something here. where should i set the username value to the ConfigMgr class?
Spring Java configuration is a newer feature that allows you to configure your Spring application using Java classes instead of XML files. Its just an alternative for XML configuration. XML way is equally feature rich.
From what I could figure out from your problem, you want to move the hardcoded values of params (numberOfCars,webSiteName.. ) outisde your configuration file.
If that is the case, you don't have to go that far.
Just use :-
<context:property-placeholder location="classpath:your_prop_file.properties" />
in your spring xml file and replace the param values like:-
<property name="webSiteName" value="${website.name}" />
You need to have a your_prop_file.properties file in your classpath with enteries like:-
website.name=New Spring Web App
You are not injecting the ConfigMgr bean that you created in XML file.
What you are doing is you are creating a new Object in controller which does not have a clue about properties file.
Now you can inject it using either #Autowired inside your controller or through xml configuration.
There are plenty of examples available in google on basic spring dependency injection.
I am creating a test application to to achieve conversion from JSON String to Employee Object before being passed to the controller.
Here are the key steps performed
Creation of Employee.java Class : Domain Object
Creation of EmployeeManagementController.java class : Spring MVC Controller for Managing Employee
Creation of EmployeeConverter.java : Custom Converter for Converting JSON String to Employee Object.
Creation of employee-servlet.xml : Spring Configuration file
Creation of web.xml : The Deployment Descriptor
Employee.java
package com.bluebench.training.domain;
import org.springframework.stereotype.Component;
#Component("employee")
public class Employee {
private PersonalDetail personal;
private EducationDetail education;
private WorkExperienceDetail experience;
// Getters and Setters
}
other domain objects are also defined
EmployeeManagementController.java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.bluebench.training.domain.Employee;
#Controller
public class EmployeeManagementController {
#RequestMapping(value="/ems/add/employee",method=RequestMethod.POST,consumes="application/json",produces="application/json")
public #ResponseBody int addEmployee(#RequestBody Employee emp){
System.out.println("RAGHAVE");
System.out.println(emp.getPersonal().getName());
int empId = 20;
return empId;
}
}
EmployeeConverter.java
package com.bluebench.training.converter;
import java.io.IOException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.core.convert.converter.Converter;
import com.bluebench.training.domain.Employee;
public class EmployeeConverter implements Converter<String,Employee>{
#Override
public Employee convert(String json) {
System.out.println("Inside convert()");
Employee emp = null;
try {
emp = new ObjectMapper().readValue(json,Employee.class);
} catch (JsonParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return emp;
}
}
employee-servlet.xml
<?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:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.bluebench.training"/>
<mvc:annotation-driven conversion-service="conversionService"/>
<mvc:default-servlet-handler/>
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="text/xml" />
<entry key="htm" value="text/html" />
</map>
</property>
<property name="defaultContentType" value="text/html"/>
</bean>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.bluebench.training.converter.EmployeeConverter"/>
</list>
</property>
</bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>PROJECT_38_SpringMVCRESTFul</display-name>
<servlet>
<servlet-name>employee</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>employee</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/employee-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
I am using Firefox RestClient to Test it.
Here is the JSON which correctly maps to the Employee object.
{"personal":{"name":"Raghave","age":33,"phoneNumber":"9594511111","address":"101, software appartment, software land , mumbai"},"education":{"qualifications":[{"insititute":"Amity University","degree":"Bachelor of Science","yearOfPassing":"2007","percentage":62.0}]},"experience":{"experience":[{"companyName":"QTBM","designation":"Programmer","years":3,"salary":12000.0},{"companyName":"Polaris","designation":"Software Developer","years":1,"salary":24000.0},{"companyName":"Ness","designation":"Senior Software Engineer","years":2,"salary":50000.0},{"companyName":"JPMC","designation":"Senior Applications Developer","years":1,"salary":120000.0}]}}
There is no Exception thrown and the controller does receive the Employee Object in the addEmployee() method. But its not via converter. The Converter is not invoked. I dont know why ? I dont want to use init binders or #Valid. I wanted to know where am i going wrong. how to make it work?
You've confused Spring's general type conversion support that uses Converters and the ConversionService with its support for HTTP message conversion that is specifically designed for converting web requests and responses and understands media types (like application/json that you're using). HTTP message conversion uses instance of HttpMessageConverter.
You don't actually need a custom converter in this case. Spring's MappingJacksonHttpMessageConverter is being used to perform the conversion automatically. The conversion can be performed automatically because, presumably (you haven't posted the setters so I'm making an educated guess), the setter methods in Employee match the JSON, i.e. setName, setAge, setPhoneNumber etc.
Arguably, the code that you've got is already working. You can safely delete your custom converter, have the same functionality, and have less code to maintain. If you really want to use a custom converter, you'll need to implement HttpMessageConverter and configure it before/in place of MappingJacksonHttpMessageConverter.
I'm using Spring 3.0.5. None of my annotated controllers are getting recognized. I have XML for my application …
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:annotation-driven/>
<context:component-scan base-package="com.myco.systems.leadsmonitor"/>
My static assets are getting picked up fine. But I'm getting a 404 when I try and reach an annotated controller …
package com.myco.systems.leadsmonitor.web.controller;
#Controller
public class HomeController {
…
#RequestMapping("/")
public void index(Model model) {
model.addAttribute("latestLeadTime", MonitorStatistics.getInstance().getLatestLeadCreationTime());
model.addAttribute("lastDBCheck", MonitorStatistics.getInstance().getLastDBCheck());
}
What else do I need to do to get my controllers picked up by Spring? Thanks, - Dave
I think the reason you are getting a 404 is because the view is not being resolved in the contoller method. By default a void return type is mapped using to a view name based on the URI, in your case since the path is "/", it probably gets resolved to "", which does not get mapped to any logical view.
Can you try a couple of things:
Start in a debug mode and see if your request is actually coming to the RequestMapping method
If it is, can you explicitly return a view name from your method and see if this view name is getting resolved to a correct view(assuming that you have a view resolver registered)
You should check both URL mappings in web.xml and view resolution configuration in your spring configuration.
One good resource is Spring By Example.org
see: http://www.springbyexample.org/examples/simple-spring-mvc-form-annotation-config-webapp.html
Another one are the tutorials available in SpringSource ToolSuite (STS), where they walk you through creating a Spring MVC web application. Those tutorials usually include the UrlRewriteFilter from tuckey.org
e.g. servlet maps to /app, then add this servlet filter
<!-- Enables clean URLs with JSP views e.g. /welcome instead of /app/welcome -->
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
<!-- <init-param>-->
<!-- <param-name>logLevel</param-name>-->
<!-- <param-value>DEBUG</param-value>-->
<!-- </init-param>-->
</filter>