My web app is not getting the datasource which was configured in server.xml. I have added the sqlconnector jar (mysql-connector-java-8.0.12) under the folder C:\wlp\usr\shared\resources\mysql
server.xml
<!-- Enable features -->
<featureManager>
<feature>cdi-1.2</feature>
<feature>jaxrs-2.0</feature>
<feature>jdbc-4.0</feature>
<feature>jndi-1.0</feature>
<feature>jpa-2.0</feature>
<feature>localConnector-1.0</feature>
<feature>servlet-3.1</feature>
</featureManager>
<!-- Declare the jar files for MySQL access through JDBC. -->
<library id="MySQLLib">
<fileset dir="${shared.resource.dir}/mysql" includes="mysql-connector-java-8.0.12.jar"/>
</library>
<!-- Declare the runtime database -->
<dataSource jndiName="AdminWeb/jdbc/AdminDS" transactional="false">
<jdbcDriver libraryRef="MySQLLib"/>
<properties databaseName="admin" password="****" portNumber="3306" serverName="localhost" user="root"/>
</dataSource>
DAO
#Resource(name = "AdminWeb/jdbc/AdminDS",lookup="AdminWeb/jdbc/AdminDS")
DataSource dataSource;
public UserEntity getAllUsers() {
UserEntity user = new UserEntity();
Connection connection = null;
try {
System.out.println("****************1");
connection = dataSource.getConnection();
System.out.println("2");
While invoking the webapp, the getconnection method throws
[ERROR ] SRVE0777E: Exception thrown by application class 'com.fist.tools.admin.dao.UserDAO.getAllUsers:25'
java.lang.NullPointerException
Could anyone please help me on this?
The dataSource/server configuration itself looks fine. #Resource can only be injected into web components/ejb components. Does the class you are injecting into fit that description?
Related
I am using Servlet 2.4, Hibernate 4.2.4 Final, c3p0 0.9.2.1, Tomcat 7.0.42, MySQL 5.6 & JSP.
I had completed development using Oracle 11gR2 DB but at a later point was asked to switch to MySQL as the Database.
I have a rather unusual problem at hand.
The problem is Multiple MySQL Process/Connections being created for every single DB request, which are neither closed nor returned to the pool despite issuing the SessionFactoryUtil.close(); which was not the case with Oracle DB.
I tested the exact same code on these two different Databases, i.e after executing a Function/Request (ex: Login)
The application when tested with Oracle (11gR2) the DB created a single connection and used it for all requests henceforth.
SELECT * FROM V$RESOURCE_LIMIT
Gives me the following Output
RESOURCE_NAME: processes
CURRENT_UTILIZATION: 32
MAX_UTILIZATION: 36
INITIAL_ALLOCATION: 300
LIMIT_VALUE: 300
No matter how many users log in the Connection pool maintains it gracefully.
Now on the other hand when the same application was run on MySQL:
I did a SHOW PROCESSLIST; on MySQL which shows two processes being created for every request; c3p0 successfully terminates one connection but the other connection remains till the DB crashes because it exceeded the max connections available.
My SessionFactoryUtil is quite simple and straightforward and is as follows:
public class SessionFactoryUtil {
private static SessionFactory sessionFactory;
public static SessionFactory getSessionFactory() {
return sessionFactory = new Configuration().configure()
.buildSessionFactory();//deprecated method not changed due to official reason
}
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
public static void close() {
if (sessionFactory != null) {
sessionFactory.close();
}
sessionFactory = null;
}
My DAO Method is as follows
public User getUserByName(String userName) throws FetchException {
User user = null;
Session session = SessionFactoryUtil.getSessionFactory().getCurrentSession();
try {
session.beginTransaction();
user = (User) session.createQuery("from User where userName = '" + userName + "'").uniqueResult();
} catch (Exception e) {
logger.info("UserDaoImpl -> getUserByName() : Error : " +e);
e.printStackTrace();
} finally {
SessionFactoryUtil.close();
}
return user;
The stack trace where c3p0 destroys a connection is as follows:
20:45:43,692 INFO com.mchange.v2.resourcepool.BasicResourcePool:1493 - A checked-out resource is overdue, and will be destroyed: com.mchange.v2.c3p0.impl.NewPooledConnection#61f31fff
20:45:43,692 INFO com.mchange.v2.resourcepool.BasicResourcePool:1496 - Logging the stack trace by which the overdue resource was checked-out.
java.lang.Exception: DEBUG STACK TRACE: Overdue resource check-out stack trace.
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:555)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:755)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:682)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
at org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider.getConnection(C3P0ConnectionProvider.java:84)
at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:292)
at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:214)
at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:157)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:67)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:160)
at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1426)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:352)
at com.sun.proxy.$Proxy7.beginTransaction(Unknown Source)
at com.demo.access.impl.ConfDaoImp.showAllEvents(ConfDaoImp.java:939)
at com.demo.business.impl.ConfServiceImpl.showAllEvents(ConfServiceImpl.java:404)
at com.demo.controller.UserController.getControls(UserController.java:112)
at com.demo.controller.UserController.validateUser(UserController.java:93)
at com.demo.controller.UserController.process(UserController.java:42)
at com.demo.controller.ApplicationServlet.process(ApplicationServlet.java:75)
at com.demo.controller.ApplicationServlet.doPost(ApplicationServlet.java:53)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at com.demo.controller.LoginFilter.doFilter(LoginFilter.java:37)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:185)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:151)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:269)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
I have read almost all the question related to this particular scenario but none seems to work, or the thread was abandoned half way, or I am missing out something; could some one please help me get through with this.
This piece of your code did the trick for me:
public static void close() {
if(sessionFactory instanceof SessionFactoryImpl) {
SessionFactoryImpl sf = (SessionFactoryImpl)sessionFactory;
ConnectionProvider conn = sf.getConnectionProvider();
if(conn instanceof C3P0ConnectionProvider) {
((C3P0ConnectionProvider)conn).close();
}
}
sessionFactory.close(); }
Until then Tomcat had (correctly) complained about memory leaks at each hot deployment. Thanks!
a few ideas:
1) you are never closing the Session you create (implicitly by asking for a "current session"). that's a straightforward reason why you might have an unreturned Connection that eventually times out.
2) you are treating your SessionFactory like a Session, building up then tearing down the whole thing (which includes a Connection pool) just to get and use one Connection. not so good. your SessionFamily should have a long lifecycle, your sessions should be for one-time, short-term use.
Its been a while since I found out the answer to my strange problem and I thought that sharing it would help most.
To start with, A couple of things i did wrong was...
Firstly, I migrated from hibernate 3.6 to 4.2 and on doing so I was still using the deprecated buildSessionFactory() method.
Secondly, I was using SessionFactoryUtil.close() after the end of each query statement in my DAO, which defeats the purpose of using Connection Pooling.
Finally, The strange issue where Oracle seems to close the connection successfully after execution of a statement whereas MySql was unable to close the same still remains a mystery.
This, I suspect happened because I was asking SessionFactoryUtil to close a connection which was originally opened by C3P0ConnectionProvider (which I believe was in-turn causing the connection leaks).
After a lot of research and looking around I re-wrote the SessionFactoryUtil as follows...
public class SessionFactoryUtil {
private static SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry;
public static SessionFactory getSessionFactory() {
Configuration configuration = new Configuration();
configuration.configure();
serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
public static Session getCurrentSession() {
if(sessionFactory == null){
getSessionFactory();
}
return sessionFactory.getCurrentSession();
}
public static void close() {
if(sessionFactory instanceof SessionFactoryImpl) {
SessionFactoryImpl sf = (SessionFactoryImpl)sessionFactory;
ConnectionProvider conn = sf.getConnectionProvider();
if(conn instanceof C3P0ConnectionProvider) {
((C3P0ConnectionProvider)conn).close();
}
}
sessionFactory.close();
}
Note that all my connections are opened by C3P0ConnectionProvider so it is only logical to close it using C3P0ConnectionProvider itself.
The following is my hibernate.cfg.xml along with c3p0 settings.
<!-- Database connection settings -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/application</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="show_sql">true</property>
<property name="format_sql">false</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.connection.release_mode">auto</property>
<!-- Create or update the database schema on startup -->
<property name="hibernate.hbm2ddl.auto">none</property>
<!-- DEPRECATED -->
<!-- <property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> -->
<!-- C3p0 connection pooling configuration -->
<property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property>
<property name="c3p0.unreturnedConnectionTimeout">600</property>
<property name="c3p0.debugUnreturnedConnectionStackTraces">false</property>
<!-- configuration pool via c3p0 -->
<property name="c3p0.acquire_increment">1</property>
<property name="c3p0.idle_test_period">600</property>
<property name="c3p0.max_size">75</property>
<property name="c3p0.max_statements">5</property>
<property name="c3p0.min_size">5</property>
<property name="c3p0.timeout">600</property>
<property name="c3p0.checkoutTimeout">6000000</property>
<property name="c3p0.testConnectionOnCheckout">false</property>
<property name="c3p0.testConnectionOnCheckin">true</property>
<!-- Mapping -->
</session-factory>
This is again one of the methods from my DAO Class...
public User getUserByName(String userName) throws FetchException {
User user = null;
Session session = SessionFactoryUtil.getCurrentSession();
try {
session.beginTransaction();
user = (User) session.createQuery("from User where userName = '" + userName + "'").uniqueResult();
session.getTransaction().commit();
} catch (Exception e) {
logger.info("UserDaoImpl -> getUserByName() : Error : " +e);
e.printStackTrace();
} finally {
}
return user;
Note that in my DAO at the finally block i don't have to close my connection any more I let c3p0 handle the connection pool.
And voila...!! The Application runs!!! Had more than 2000 transaction hits on a single day in a span of 2 hours.
I hope this helps noob hibernate users like me.
I have been trying to connect a smartgwt client app to a MySQL server.
I have already created server side implementation 'MySQLConection'
and client side synchronous and asynchrous interface.
I created a RPC object in my entry point class but every time I try to launch it I am getting
Line 13: No source code is available for type java.lang.ClassNotFoundException; did you forget to inherit a required module?
Line 13: No source code is available for type java.sql.SQLException; did you forget to inherit a required module?
at line 13 i have
ArrayList onLoad() throws ClassNotFoundException, SQLException, IOException;
I have already added tag in *.gwt.xml file and
also include the jar files
Here is my Connection code
public Connection MySQLConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
Connection connection = null;
try {
connection = (Connection) DriverManager.getConnection(url, user, pass);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
return connection;
}
Here is my *gwt.xml code
<module rename-to='Abc'>
<inherits name="com.google.gwt.user.User" />
<inherits name="com.google.gwt.user.theme.standard.Standard" />
<inherits name="com.smartgwt.SmartGwt" />
<entry-point class="com.xyz.client.Abc" />
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> -->
<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> -->
<!-- Other module inherits -->
<!-- <inherits name="com.smartgwt.SmartGwt"/> -->
<inherits name="com.smartgwt.SmartGwtNoTheme" />
<inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlue" />
<inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlueResources" />
<servlet class="com.xyz.server.MySQLConnection" path="/MySQLConnection" />
<source path='client' />
<source path='shared' />
</module>
I am calling the connection in entry class as follows
rpc = (DBConnectionAsync) GWT.create(DBConnection.class);
You're getting this problem because you must be writing some code that accesses ClassNotFoundException, SQLException, IOException, in a class that is contained in the client folder.
Any class in this folder cannot access every Java class.
Your server side logic should be written in a class that is contained by server folder.
To understand the project structure of GWT, have a look at the following link:
https://developers.google.com/web-toolkit/doc/1.6/DevGuideOrganizingProjects#DevGuideDirectoriesPackageConventions
To understand why you cannot use every class in client package, have a look at the following link:
GWT - did you forget to inherit a required module?
I am trying to test an Stateless bean with JUnit in netbeans. This bean uses an EntityManager.
#Stateless
public class myEjb{
#PersistenceContext
private EntityManager em;
public MyResult getResult(){
return em.find(...);
}
}
Then I write a test class.
public class myTest{
private static EJBContainer ec;
private static Context ctx;
#BeforeClass
public static void setUpClass(){
ec = EJBContainer.createEJBContainer();
ctx = ec.getContext();
}
....
}
When I run the test, it does not work. I obtain the following message:
Invalid resource : mydb__pm
The error occurs when this line is executed:
ec = EJBContainer.createEJBContainer();
If a change my bean by removing the entity manager, it works. So, it seems that I have a problem with the entity manager.
My persistence.xml file is simple:
<persistence version="2.0" ...>
<persistence-unit name="MetisDemoPU" transaction-type="JTA">
<jta-data-source>MyDb</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
</persistence-unit>
</persistence>
Finally, I create a JSF managed bean that called my EJB (which uses the entity manager) and it works.
#ManagedBean
#RequestScoped
public class myManagedBean{
#EJB
private OfferEjb offerEjb;
...
}
Any help would be appreciated!
Ok, I find a solution to my problem. I am now able to use JUnit to test my session bean with a persistence context.
I am not a specialist, so my explanation will probably not be complete.
With netbeans 7.2, there is an embedded glassfish server which is used for the test. It is necessary to configure the jdbc parameters in the domain.xml file and then for me it works.
On my computer, this file is under
C:\Program Files\glassfish-3.1.2.2\glassfish\domains\domain\
I just add a jdbc connection pool and a jdvc jndi.
This article contains more details.
I use shiro to implement authentication for my CXF web service. I am using a jdbc-Realm and configured it with the help of an ini-file (attached below). The authentication data is persisted in the same database like the other data I need, but for the rest of the system I use a properties-file (can be found below, too) to provide the connection-information.
Now obviously the data for the datasource in both cases is the same, but I do not seem to find a way to resolve this code duplication. Does anybody more experienced with the development of webapplications have a solution? I could change both, the config of shiro and of the rest of the system, if it would help.
Thanks in advance,
zakum
shiro.ini:
[main]
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.authenticationQuery = SELECT password FROM users WHERE username = ?;
ds = org.postgresql.ds.PGSimpleDataSource
ds.user = postgres
ds.password = password
ds.databaseName = servicedb
ds.serverName = localhost
ds.portNumber = 5432
jdbcRealm.dataSource = $ds
securityManager.realms = $jdbcRealm
service.properties:
db. It looks like:
db.name = servicedb
db.user = postgres
db.password = password
db.url = //localhost:5432/
Use a container like Spring to configure Shiro and your JDBC connections. Then you can pass the dataSource as a reference into the JDBCRealm.
<bean id="dataSourceBean" class="com.apache.commons.dbcp.BasicDataSource>
<property name="driverClassName" value"class for driver"/>
... more setup for the data source ...
</bean>
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="dataSource" ref="dataSourceBean"/>
<property name="permissionsLookupEnabled" value="true"/>
<property name="authenticationQuery" value="SELECT password FROM users WHERE username = ?"/>
</bean>
You could automatically replace tokens during your build process using Ant or Maven (among others).
Here's an example using Ant:
<copy file="shiro.template.ini" tofile="shiro.ini" overwrite="true" />
<replace file="shiro.ini" token="#DB_NAME#" value="servicedb"/>
In the *.template.* files you'd use tokens:
ds.databaseName = #DB_NAME#
which would get replaced with the real values during the build process:
ds.dataBaseName = servicedb
(and it would obviously be better to specify the tokens/values in a configuration file and apply the replacements to a list of files using globbing patterns)
Ant: https://ant.apache.org/manual/Tasks/replace.html
Maven: https://code.google.com/p/maven-replacer-plugin/
Although this is an old question, I had a similar query and I solved it the following way. Hope it will be helpful to others.
Shiro ini can be used to configure any class. For data source, I have written my application specific class as this.
import javax.sql.DataSource;
public class MyConfig
{
private static DataSource dataSource;
public void setDataSource(Object ds)
{
dataSource = (DataSource)ds;
}
public static DataSource getDataSource() // your application will use this method to get data source.
{
return dataSource;
}
}
Now in the ini file, I passed the same datasource reference to both, the jdbc realm and my class.
myConfig = my.package.MyConfig
myConfig.dataSource = $ds
I'm not sure whether this is a misconfiguration on my part, a misunderstanding of what can be accomplished via #ModelAttribute and automatic JSON content conversion, or a bug in either Spring or Jackson. If it turns out to be the latter, of course, I'll file an issue with the appropriate folks.
I've encountered a problem with adding a #ModelAttribute to a controller's handler method. The intent of the method is to expose a bean that's been populated from a form or previous submission, but I can reproduce the issue without actually submitting data into the bean.
I'm using the Spring mvc-showcase sample. It's currently using Spring 3.1, but I first encountered, and am able to reproduce, this issue on my 3.0.5 setup. The mvc-showcase sample uses a pretty standard servlet-context.xml:
servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
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">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven conversion-service="conversionService">
<argument-resolvers>
<beans:bean class="org.springframework.samples.mvc.data.custom.CustomArgumentResolver"/>
</argument-resolvers>
</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>
<!-- Imports user-defined #Controller beans that process client requests -->
<beans:import resource="controllers.xml" />
<!-- Only needed because we install custom converters to support the examples in the org.springframewok.samples.mvc.convert package -->
<beans:bean id="conversionService" class="org.springframework.samples.mvc.convert.CustomConversionServiceFactoryBean" />
<!-- Only needed because we require fileupload in the org.springframework.samples.mvc.fileupload package -->
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
</beans:beans>
The controllers.xml referenced in the file simply sets up the relevant component-scan and view-controller for the root path. The relevant snippet is below.
controllers.xml
<!-- Maps '/' requests to the 'home' view -->
<mvc:view-controller path="/" view-name="home"/>
<context:component-scan base-package="org.springframework.samples.mvc" />
The test bean which I am attempting to deliver is a dead-simple POJO.
TestBean.java
package org.springframework.samples.mvc.test;
public class TestBean {
private String testField = "test#example.com";
public String getTestField() {
return testField;
}
public void setTestField(String testField) {
this.testField = testField;
}
}
And finally, the controller, which is also simple.
TestController.java
package org.springframework.samples.mvc.test;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
#RequestMapping("test/*")
public class TestController {
#ModelAttribute("testBean")
public TestBean getTestBean() {
return new TestBean();
}
#RequestMapping(value = "beanOnly", method = RequestMethod.POST)
public #ResponseBody
TestBean testBean(#ModelAttribute("testBean") TestBean bean) {
return bean;
}
#RequestMapping(value = "withoutModel", method = RequestMethod.POST)
public #ResponseBody
Model testWithoutModel(Model model) {
model.addAttribute("result", "success");
return model;
}
#RequestMapping(value = "withModel", method = RequestMethod.POST)
public #ResponseBody
Model testWithModel(Model model, #ModelAttribute("testBean") TestBean bean) {
bean.setTestField("This is the new value of testField");
model.addAttribute("result", "success");
return model;
}
}
If I call the controller via the mapped path /mvc-showcase/test/beanOnly, I get a JSON representation of the bean, as expected. Calling the withoutModel handler delivers a JSON representation of the Spring Model object associated with the call. It includes the implicit #ModelAttribute from the initial declaration in the return value, but the bean is unavailable to the method. If I wish to process the results of a form submission, for example, and return a JSON response message, then I need that attribute.
The last method adds the #ModelAttribute, and this is where the trouble comes up. Calling /mvc-showcase/test/withModel causes an exception.
In my 3.0.5 installation, I get a JsonMappingException caused by a lack of serializer for FormattingConversionService. In the 3.1.0 sample, the exception is caused by lack of serializer for DefaultConversionService. I'll include the 3.1 exception here; it seems to have the same root cause, even if the path is a bit different.
3.1 org.codehaus.jackson.map.JsonMappingException
org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.springframework.format.support.DefaultFormattingConversionService and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.validation.support.BindingAwareModelMap["org.springframework.validation.BindingResult.testBean"]->org.springframework.validation.BeanPropertyBindingResult["propertyAccessor"]->org.springframework.beans.BeanWrapperImpl["conversionService"])
at org.codehaus.jackson.map.ser.StdSerializerProvider$1.failForEmpty(StdSerializerProvider.java:89)
at org.codehaus.jackson.map.ser.StdSerializerProvider$1.serialize(StdSerializerProvider.java:62)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:272)
at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:147)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:272)
at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:147)
at org.codehaus.jackson.map.ser.MapSerializer.serializeFields(MapSerializer.java:207)
at org.codehaus.jackson.map.ser.MapSerializer.serialize(MapSerializer.java:140)
at org.codehaus.jackson.map.ser.MapSerializer.serialize(MapSerializer.java:22)
at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:315)
at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:242)
at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1030)
at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.writeInternal(MappingJacksonHttpMessageConverter.java:153)
at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:181)
at org.springframework.web.servlet.mvc.method.annotation.support.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:121)
at org.springframework.web.servlet.mvc.method.annotation.support.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101)
at org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:81)
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:64)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodAdapter.invokeHandlerMethod(RequestMappingHandlerMethodAdapter.java:505)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodAdapter.handleInternal(RequestMappingHandlerMethodAdapter.java:468)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at
...
So, is there some configuration I am missing that should allow the Jackson converter to properly handle a response derived from a handler with #ModelAttribute in the method signature? If not, any thoughts as to whether this is more likely a Spring bug or a Jackson bug? I'm leaning toward Spring, at this point.
It looks like a Spring config problem, when serializing to JSON the DefaultFormattingConversionService is empty and Jackson (by default) will throw an exception if a bean is empty see FAIL_ON_EMPTY_BEANS in the features documentation. But I am not clear why the bean is empty.
It should work if you set FAIL_ON_EMPTY_BEANS to false, but still doesn't really explain why it is happening in the first place.
DefaultFormattingConversionService is new to 3.1 - it extends the FormattingConversionService which explains the different exceptions between 3.0.5 and 3.1.
I do not think it is a Jackson problem, although a new version of Jackson (1.8.0) was released only 3 days ago so you could try that also.
I will try to reproduce this locally.