Specify a custom web.xml to an embedded tomcat - spring

Is there a way to specify a different web.xml from the standard WEB-INF/web.xml when using an embedded tomcat instance?
I would like to put a web.xml in my src/test/resources (or some other area) and refer to that web.xml when starting the embedded tomcat.
Here is my existing code to start the tomcat instance
tomcat = new Tomcat();
String baseDir = ".";
tomcat.setPort(8080);
tomcat.setBaseDir(baseDir);
tomcat.getHost().setAppBase(baseDir);
tomcat.getHost().setAutoDeploy(true);
tomcat.enableNaming();
Context ctx = tomcat.addWebApp(tomcat.getHost(), "/sandbox-web", "src\\main\\webapp");
File configFile = new File("src\\main\\webapp\\META-INF\\context.xml");
ctx.setConfigFile(configFile.toURI().toURL());
tomcat.start();
I am starting this server from a tomcat instance and I would like to do the following when running unit tests
turn off the contextConfigLocation
specify a custom ContextLoaderListener that sets the parent ApplicationContext of the embedded tomcat.
This file might be specified like so:
File webXmlFile = new File("src\\test\\resources\\embedded-web.xml");
Edit
After much frustration I realized that no matter what I do, I cannot persuade tomcat from looking in WEB-INF for web.xml. It appears that I must ignore the web.xml altogether and set the items in the web.xml programmatically.
I ended up with this configuration:
cucumber.xml for configuring tests
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="applicationContextProvider" class="ca.statcan.icos.sandbox.ApplicationContextProvider"/>
<bean id="sandBoxDataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:testdb;shutdown=true;" />
<property name="username" value="SA" />
<property name="password" value="" />
</bean>
<!-- Support for JPA related annotation support (#PersistenceUnit and #PersistenceContext) -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<!-- JTA Configuration -->
<bean id="jtaTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close">
<property name="forceShutdown"><value>true</value></property>
</bean>
<bean id="jtaUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" />
<bean id="springTransactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="jtaTransactionManager" />
<property name="userTransaction" ref="jtaUserTransaction" />
</bean>
<!-- JPA Entity Manager configuration -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
lazy-init="true">
<property name="persistenceUnitName" value="sandBox" />
<property name="dataSource" ref="sandBoxDataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="SQL_SERVER" />
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
<property name="jpaPropertyMap">
<props>
<prop key="hibernate.archive.autodetection">class</prop>
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<!-- Second Level Cache : EHCache in dev
<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop> -->
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<import resource="classpath:META-INF/applicationContext-core.xml" />
<import resource="classpath:META-INF/applicationContext-web.xml" />
</beans>
applicationContext-core.xml - where the services are configured
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd"
default-autowire="byName">
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath*:META-INF/fms-local.properties" />
<property name="systemPropertiesModeName">
<value>SYSTEM_PROPERTIES_MODE_OVERRIDE</value>
</property>
</bean>
<!--
Classpath scanning to load all the service classes
-->
<context:component-scan base-package="ca.statcan"
use-default-filters="false">
<context:include-filter type="regex" expression="ca\.statcan\.icos.*\.service\..*Service" />
<context:include-filter type="regex" expression="ca\.statcan\.icos.*\.builders\..*Builder" />
</context:component-scan>
<!--
Spring TransactionManager
-->
<tx:advice id="txAdvice" transaction-manager="springTransactionManager">
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true" propagation="SUPPORTS" isolation="DEFAULT"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS" isolation="DEFAULT"/>
<!-- other methods use the default transaction settings -->
<tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!--
AOP Weaving for all Service methods
-->
<aop:config proxy-target-class="true">
<aop:pointcut id="icosServiceMethods" expression="execution(* ca.statcan.icos..*.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="icosServiceMethods" />
</aop:config>
</beans>
Custom ContextLoaderListener
public class EmbeddedContextLoaderListener extends ContextLoaderListener {
#Override
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
GenericWebApplicationContext context = new GenericWebApplicationContext(sc);
context.setParent(ApplicationContextProvider.getApplicationContext());
return context;
}
#Override
protected ApplicationContext loadParentContext(ServletContext servletContext) {
return ApplicationContextProvider.getApplicationContext();
}
}
Modified Embedded Tomcat Wrapper
public class EmbeddedTomcat {
/** Log4j logger for this class. */
#SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(EmbeddedTomcat.class);
private Tomcat tomcat;
public void start() {
try {
tomcat = new Tomcat();
String baseDir = ".";
tomcat.setPort(8080);
tomcat.setBaseDir(baseDir);
tomcat.getHost().setAppBase(baseDir);
tomcat.getHost().setDeployOnStartup(true);
tomcat.getHost().setAutoDeploy(true);
tomcat.enableNaming();
Context context = tomcat.addContext("/sandbox-web", "src\\main\\webapp");
Tomcat.initWebappDefaults(context);
configureSimulatedWebXml(context);
LOG.info("Starting tomcat in: " + new File(tomcat.getHost().getAppBase()).getAbsolutePath());
tomcat.start();
} catch (LifecycleException e) {
throw new RuntimeException(e);
}
}
public void stop() {
try {
tomcat.stop();
tomcat.destroy();
FileUtils.deleteDirectory(new File("work"));
FileUtils.deleteDirectory(new File("tomcat.8080"));
} catch (LifecycleException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void deploy(String appName) {
tomcat.addWebapp(tomcat.getHost(), "/" + appName, "src\\main\\webapp");
}
public String getApplicationUrl(String appName) {
return String.format("http://%s:%d/%s", tomcat.getHost().getName(),
tomcat.getConnector().getLocalPort(), appName);
}
public boolean isRunning() {
return tomcat != null;
}
private void configureSimulatedWebXml(final Context context) {
// Programmatically configure the web.xml here
context.setDisplayName("Sandbox Web Application");
context.addParameter("org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG", "/WEB-INF/tiles-defs.xml,/WEB-INF/tiles-sandbox.xml");
final FilterDef struts2Filter = new FilterDef();
struts2Filter.setFilterName("struts2");
struts2Filter.setFilterClass("org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter");
struts2Filter.addInitParameter("actionPackages", "ca.statcan.icos.sandbox.web");
context.addFilterDef(struts2Filter);
final FilterMap struts2FilterMapping = new FilterMap();
struts2FilterMapping.setFilterName("struts2");
struts2FilterMapping.addURLPattern("/*");
context.addFilterMap(struts2FilterMapping);
context.addApplicationListener("org.apache.tiles.web.startup.TilesListener");
context.addApplicationListener("ca.statcan.icos.sandbox.EmbeddedContextLoaderListener");
context.addWelcomeFile("index.jsp");
}
}
Step definitions
public class StepDefs {
#Autowired
protected EmployeeEntityService employeeEntityService;
#Given("^the following divisions exist$")
public void the_following_divisions_exist(DataTable arg1) throws Throwable {
final Employee employee = new Employee(3, "Third", "John", null, "613-222-2223");
employeeEntityService.persistEmployee(employee);
}
#Given("^there are no existing surveys$")
public void there_are_no_existing_surveys() throws Throwable {
}
#When("^I register a new survey with the following information$")
public void I_register_a_new_survey_with_the_following_information(DataTable arg1) throws Throwable {
Capabilities capabilities = DesiredCapabilities.htmlUnit();
final HtmlUnitDriver driver = new HtmlUnitDriver(capabilities);
driver.get("http://localhost:8080/sandbox-web/myFirst");
}
#Then("^the surveys are created$")
public void the_surveys_are_created() throws Throwable {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
#Then("^a confirmation message is displayed saying: \"([^\"]*)\"$")
public void a_confirmation_message_is_displayed_saying(String arg1) throws Throwable {
// Express the Regexp above with the code you wish you had
throw new PendingException();
}
}
Action class
#Results({ #Result(name = "success", location = "myFirst.page", type = "tiles") })
#ParentPackage("default")
#Breadcrumb(labelKey = "ca.statcan.icos.sandbox.firstAction")
#SuppressWarnings("serial")
public class MyFirstAction extends HappyfActionSupport {
private List<Employee> employees;
#Autowired
private EmployeeEntityService employeeEntityService;
#Override
public String execute() {
employees = employeeEntityService.getAllEmployee();
if (employees.size() == 0) {
// persist data in memory
final Employee employee1 = new Employee(1, "First", "John", null, "613-222-2222");
employeeEntityService.persistEmployee(employee1);
final Employee employee2 = new Employee(2, "Second", "John", null, "613-222-2223");
employeeEntityService.persistEmployee(employee2);
employees = employeeEntityService.getAllEmployee();
}
return SUCCESS;
}
public List<Employee> getEmployees() {
return employees;
}
}
With this, the embedded tomcat starts correctly and all seems to go well until I try to navigate to a web page. In the StepDefs class, the EmployeeEntityService is injected correctly. However, in the Action class, EmployeeEntityservice is not injected (it remains null).
According to my knowledge, I am setting the parent ApplicationContext for the embedded Tomcat correctly. So why isn't the server using the parent context to get the EmployeeEntityService?

I was stuck with a similar problem and the solution for using an alternative web.xml is simpler than one would dare to think:
2 lines:
Context webContext = tomcat.addWebapp("/yourContextPath", "/web/app/docroot/");
webContext.getServletContext().setAttribute(Globals.ALT_DD_ATTR, "/path/to/custom/web.xml");
Voila! The magic happens in org.apache.catalina.startup.ContextConfig#getWebXmlSource
Disclaimer: Tested on Tomcat 7.0.42

Related

Spring injected bean is null

I am using Spring Framework / Data / HATEOAS and trying to add Dozer.
I have the following bean in my spring-config.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:data="http://www.springframework.org/schema/data/jpa"
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/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="POSTGRESQL" />
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="dataSource" />
<property name="jpaDialect" ref="jpaDialect" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost:5432/cp" />
<property name="username" value="cp_user" />
<property name="password" value="+JMJ+pw0m2d" />
</bean>
<context:component-scan base-package="com.mydomain.data.assembler" />
<data:repositories base-package="com.mydomain.repository" />
<mvc:annotation-driven />
<bean id="dozerFactory" class="org.dozer.spring.DozerBeanMapperFactoryBean" scope="singleton">
<property name="mappingFiles" value="classpath*:/*mapping.xml"/>
</bean>
</beans>
And the following assembler:
#Component
public class UserResourceAssembler {
#Inject
private Mapper dozerBeanMapper;
public UserResource toResource(User user) {
UserResource resource = dozerBeanMapper.map(user, UserResource.class);
resource.add(linkTo(methodOn(UserController.class).get(user.getId())).withSelfRel());
return resource;
}
public User toEntity(UserResource resource) {
User user = dozerBeanMapper.map(resource, User.class);
return user;
}
}
So, - I'm very new to beans and injection - but I guess that the factory bean is ?supposed? to inject the Mapper. But the Mapper is definitely null. I know I'm not doing this right, but what am I doing wrong?
Spring injects its beans into spring managed beans. You are using an unmanaged static context. Change UserResourceAssembler into a managed bean as well:
#Component
public class UserResourceAssembler {
#Inject
private Mapper dozerBeanMapper;
public UserResource toResource(User user) {
}
public User toEntity(UserResource resource) {
}
}
See why can't we autowire static fields in spring.
I would have preferred something like the above. But then I read:
Dozer Singleton Startup Bean injetced as Null
That worked. Here was my implementation.
I removed the bean from spring-config, and the context scan.
I added this class:
#Singleton
public class DozerInstantiator {
public static DozerBeanMapper getInstance(){
return MapperHolder.instance;
}
private static class MapperHolder{
static final DozerBeanMapper instance = new DozerBeanMapper();
}
}
And updated my assembler like this:
public class UserResourceAssembler {
private DozerBeanMapper mapper;
public UserResourceAssembler() {
mapper = DozerInstantiator.getInstance();
}
public UserResource toResource(User user) {
UserResource resource = mapper.map(user, UserResource.class);
resource.add(linkTo(methodOn(UserController.class).get(user.getId())).withSelfRel());
return resource;
}
public User toEntity(UserResource resource) {
User user = mapper.map(resource, User.class);
return user;
}
}

How to get spring configuration from database

I'm using Eclipse Juno, JDK 7, tomcat 7.0.29, hibernate 4.1.5 and Spring 3.1.2.
I'm trying to load Spring configuration info from database. This is what I could do until now:
WEB.XML
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/context-beans.xml
</param-value>
</context-param>
jdbc.properties
jdbc.driver_class=com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc.url=jdbc:sqlserver://localhost;databaseName=mydbname;
jdbc.username=<my_usr>
jdbc.password=<my_pwd>
context-beans.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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:security="http://www.springframework.org/schema/security"
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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-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/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.1.xsd">
<!-- Configure which package will contain our components -->
<context:component-scan base-package="com.xxx" />
<context:annotation-config />
<mvc:annotation-driven />
<mvc:resources mapping="/static/**" location="/static/" />
<tx:annotation-driven />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="appPersistenceUnit" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" p:driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
p:url="jdbc:sqlserver://localhost;databaseName=appDB" p:username="theUserName" p:password="thePassword" destroy-method="close"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" lazy-init="false" >
<property name="dataSource" ref="dataSource" />
<property name="lazyInit" value="false" />
</bean>
<bean id="appProperties" class="com.xxx.web.util.MyAppPropertyPlaceholderConfigurer">
<property name="nameColumn" value="key" />
<property name="valueColumn" value="value" />
<property name="tableName" value="app_params" />
<property name="whereClause" value="param_type = 'GLOBAL'" />
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
<!-- Files path configuration -->
<mvc:resources mapping="/iconfiles/**" location="file:/${PATH.ICON_FILES}" />
</beans>
Please consider table app_params contains 1 record where key="PATH.ICON_FILES", value="c:/temp/icons/" and param_type="GLOBAL".
MyAppPropertyPlaceholderConfigurer
package com.xxx.web.util;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
public class MyAppPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private JdbcTemplate jdbcTemplate;
private String nameColumn;
private String valueColumn;
private String tableName;
private String whereClause;
public MyAppPropertyPlaceholderConfigurer() {
super();
setPlaceholderPrefix("${");
}
#Override
protected void loadProperties(final Properties props) throws IOException {
if (null == props) {
throw new IOException("No properties passed by Spring framework - cannot proceed.");
}
String sql = String.format("SELECT [%s], [%s] FROM [%s] %s", nameColumn, valueColumn, tableName, whereClause);
logger.info("Reading configuration properties from database. Query=" + sql);
try {
jdbcTemplate.query(sql, new RowCallbackHandler() {
#Override
public void processRow(ResultSet rs) throws SQLException {
String name = rs.getString(nameColumn);
String value = rs.getString(valueColumn);
if (null == name || null == value) {
throw new SQLException("Configuration database contains empty data. Name='" + (name == null ? "" : name)
+ "', Value='" + (value == null ? "" : value) + "'.");
}
props.setProperty(name, value);
}
});
} catch (Exception e) {
logger.fatal("There is an error in either 'application.properties' or the configuration database.");
throw new IOException(e);
}
if (props.size() == 0) {
logger.fatal("The configuration database could not be reached or does not contain any properties in '" + tableName
+ "'.");
} else {
logger.info("Application config info loaded from configuration database.");
}
}
#Override
protected String convertPropertyValue(String originalValue) {
if (originalValue.startsWith("!!")) {
// TODO ex: return EncryptUtil.decrypt(originalValue); see org.owasp.esapi.reference.crypto.EncryptedPropertiesUtils
return originalValue;
}
return originalValue;
}
public void setJdbcTemplate(JdbcTemplate newJdbcTemplate) {
this.jdbcTemplate = newJdbcTemplate;
}
public void setNameColumn(String newNameColumn) {
this.nameColumn = newNameColumn;
}
public void setValueColumn(String newValueColumn) {
this.valueColumn = newValueColumn;
}
public void setTableName(String newTableName) {
this.tableName = newTableName;
}
public void setWhereClause(String newWhereClause) {
if (newWhereClause == null || newWhereClause.trim().length() == 0) {
this.whereClause = "";
} else {
if (newWhereClause.trim().toUpperCase().startsWith("WHERE ")) {
this.whereClause = newWhereClause;
} else {
this.whereClause = "WHERE " + newWhereClause;
}
}
}
}
Doing it this way works as expected and I have variable ${PATH.ICON_FILES} properly resolved.
However, I don't want to have my dataSource bean with username, password and all stuff hard coded on the XML file. I would like to load it from another property file, preferentially encrypted.
How do I get this done?
I already tried to add another PropertyPlaceholderConfigurer with jdbc configuration (plain text at this time) by adding the code below into context-beans.xml:
<bean id="jdbcConfigurer" name="jdbcConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
Then I changed dataSource bean to read from there:
<bean id="dataSource" name="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" p:driverClassName="${jdbc.driver_class}"
p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" destroy-method="close" />
When I try to run I get the following error:
[2012-10-10 11:33:48,744] WARN (SQLErrorCodesFactory.java:227) - Error while extracting database product name - falling back to empty error codes
org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class '${jdbc.driver_class}'
Any idea what I'm doing wrong here? Looks like it is instantiating MyAppPropertyPlaceholderConfigurer prior to getting the values from the regular PropertyPlaceholderConfigurer...
Thanks.

Change SessionFactory datasource jdbcurl late in runtime

I'm writing a desktop java application for an environment without a network connection. I'm trying to store the application data as securely as I can in an encrypted in-process hsqldb, with an unencrypted user information hsqldb. Hsqldb requires that the crypto_key be set in the jdbcurl when the connection is created. My application uses hibernate to do persistence and Spring to do configuration and injection.
My current scheme is to store username, password hash, salt and the encrypted database's crypto_key in the unencrypted user table. The crypto_key is protected by an asymmetric encryption using the user's password as the key. Thus, the application doesn't know what the crypto_key for the application data is until after it has been running long enough to load a gui, and authenticate the user.
Here is my current applicationContext.xml. Spring uses it to get Hibernate going and functioning.
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="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/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<context:component-scan base-package="com.company.domain" />
<context:component-scan base-package="com.company.service" />
<tx:annotation-driven />
<bean id="userDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url"
value="jdbc:hsqldb:./ReviewDatabase/users" />
<property name="username" value="reviewer" />
<property name="password" value="$kelatonKey" />
</bean>
<bean id="mainDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url"
value="jdbc:hsqldb:./ReviewDatabase/data" /> <!-- TODO: ;crypt_key=;crypt_type=AES -->
<property name="username" value="reviewer" />
<property name="password" value="$kelatonKey" />
</bean>
<bean id="userSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="userDataSource" />
<property name="annotatedClasses">
<list>
<value>com.company.domain.AppUser</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="mainSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="mainDataSource" />
<property name="annotatedClasses">
<list>
<!-- <value>com.companu.domain.Person</value> -->
<!-- <value>com.company.domain.Thing</value> -->
<!-- <value>com.company.domain.Thing1</value> -->
<!-- <value>com.company.domain.Thing2</value> -->
<!-- <value>com.company.domain.Review</value> -->
</list>
</property>
<property name="hibernateProperties">
<props>
<pro key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="mainTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="mainSessionFactory" />
</bean>
<bean id="userTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="userSessionFactory" />
</bean>
</beans>
Here's an example of a class where I would like to have the SessionFactory injected
#Repository("ReviewDao")
public class HibernateReviewDao implements ReviewDao{
private SessionFactory mainSessionFactory;
#Autowired
public void setMainSessionFactory(
SessionFactory mainSessionFactory){
this.mainSessionFactory = mainSessionFactory;
}
#Override
#Transactional(value = "mainTransactionManager")
public void store(Review review) {
mainSessionFactory.getCurrentSession().saveOrUpdate(review);
}
#Override
#Transactional(value = "mainTransactionManager")
public void delete(Long reviewId) {
Review review = (Review)mainSessionFactory.getCurrentSession()
.get(Review.class, reviewId);
mainSessionFactory.getCurrentSession().delete(review);
}
}
And finally, here's what I've tried to do after authenticating the user and getting that crypto_key.
String jdbcUrl = "jdbc:hsqldb:./ReviewDatabase/data2;crypt_key=" + secret + ";crypt_type=AES";
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySetting("hibernate.dialect", "org.hibernate.dialect.HSQLDialect")
.applySetting("hibernate.show_sql", "true")
.applySetting("hibernate.hbm2ddl.auto","update")
.applySetting("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver")
.applySetting("hibernate.connection.url", jdbcUrl)
.applySetting("hibernate.connection.username", "reviewer")
.applySetting("hibernate.connection.password", "$kelatonKey")
.buildServiceRegistry();
SessionFactory mainSessionFactory = new MetadataSources(serviceRegistry)
.addAnnotatedClass(com.company.domain.Review.class)
.addAnnotatedClass(com.company.domain.Person.class)
.addAnnotatedClass(com.company.domain.Thing.class)
.addAnnotatedClass(com.company.domain.Thing1.class)
.addAnnotatedClass(com.company.domain.Thing2.class)
.buildMetadata()
.buildSessionFactory();
org.springframework.orm.hibernate4.HibernateTransactionManager htm =
(HibernateTransactionManager)context.getBean("mainTransactionManager");
context.getAutowireCapableBeanFactory().initializeBean(mainSessionFactory, "mainSessionFactory");
htm.setSessionFactory(mainSessionFactory);
However, with that, the first query to the object above results in org.hibernate.HibernateException: No Session found for current thread
How can I change the jdbcurl long after hibernate has initialized, dependencies have been injected and other various kinds of tom-foolery has occurred?
I've been putting this part of development off, hoping Google would eventually come through, but I'm out of ideas to search for. All answers will be accepted with sheepish humility :)
I wonder if this might help, Can I replace a Spring bean definition at runtime? , you could dummy up the bean properties to start with and then change the bean in runtime.
So, the missing bit of the recipe was LocalSessionFactoryBean. It got the sessionFactory setup so I could just replace the sessionFactories that are created at initialization.
Here's the code I had to change from the question
org.springframework.orm.hibernate4.HibernateTransactionManager htm =
(HibernateTransactionManager)context.getBean("mainTransactionManager");
Class<?>[] classes = new Class<?>[5];
classes[0] = com.company.domain.Thing1.class;
classes[1] = com.company.domain.Thing2.class;
classes[2] = com.company.domain.Person.class;
classes[3] = com.company.domain.Thing.class;
classes[4] = com.company.domain.Review.class;
String jdbcUrl = "jdbc:hsqldb:./ReviewDatabase/data3;crypt_key=" + secret + ";crypt_type=AES";
java.util.Properties hibernateProperties = new java.util.Properties();
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
hibernateProperties.setProperty("hibernate.show_sql", "true");
hibernateProperties.setProperty("hibernate.hbm2ddl.auto","update");
hibernateProperties.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
hibernateProperties.setProperty("hibernate.connection.url", jdbcUrl);
hibernateProperties.setProperty("hibernate.connection.username", "reviewer");
hibernateProperties.setProperty("hibernate.connection.password", "$kelatonKey");
LocalSessionFactoryBean slfb = new LocalSessionFactoryBean();
slfb.setHibernateProperties(hibernateProperties);
slfb.setAnnotatedClasses(classes);
try {
slfb.afterPropertiesSet();
} catch (IOException e) {
Log.warn("Cannot connection to application database");
Log.write(e.getLocalizedMessage());
Log.write(e.getStackTrace());
return;
}
SessionFactory mainSessionFactory = slfb.getObject();
context.getAutowireCapableBeanFactory().initializeBean(mainSessionFactory, "mainSessionFactory");
htm.setSessionFactory(mainSessionFactory);
for(ListenForNewSessionFactory dao : daos){
dao.setNewSessionFactory(mainSessionFactory);
}
I had each Dao implement an interface to set the sessionFactory, and had each of them add themselves to a static list on initialization. It's not very reusable, but it works.
I used the following hack - wherever I needed a SessionFactory, I used a SessionFactoryFactory (below) instead - delegates the only SessionFactory method I actually use.
#Component
public class SessionFactoryFactory {
#Autowired
private LocalSessionFactoryBean sessionFactoryBean;
#Autowired
private DriverManagerDataSource dataSource;
private SessionFactory sessionFactory;
private SessionFactory getSessionFactory() {
if (null == sessionFactory) {
sessionFactory = sessionFactoryBean.getObject();
}
return sessionFactory;
}
public Session openSession() {
return getSessionFactory().openSession();
}
public void updateDataSourceUrl() throws IOException {
sessionFactory = null;
sessionFactoryBean.afterPropertiesSet();
}
}

Injection of the sessionFactory not working

I'm new with Spring and I want to inject the sessionFactory and its not working at all. I'm starting a web server Jetty and then I load the context. After i start a GWT web application, I make a call on the server side and I try to get some info but I get a null pointer. There is no error at all so it make it difficult to know where the problem is. I know its suppose to work because I saw it working on a project I worked on some times ago. Any help will be appreciate. (Sorry for my possibly bad english)
Here is the context.xml :
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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/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/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">
<!-- Standard spring initialization -->
<context:component-scan base-package="com.test">
</context:component-scan>
<tx:annotation-driven transaction-manager="txManager"/>
<!-- Connection to the database-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-
method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="123456789" />
</bean>
<!-- Hibernate session factory-->
<bean id="jpaSessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.domain"/>
<property name="namingStrategy" >
<bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- Hibernate session factory -->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="jpaSessionFactory" />
</bean>
</beans>
Here is the main :
public static void main(String[] args) {
ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(new Handler[]
{ new AppContextBuilder().buildWebAppContext()});
final JettyServer jettyServer = new JettyServer();
jettyServer.setHandler(contexts);
Runnable runner = new Runnable() {
#Override
public void run() {
new ServerRunner(jettyServer);
}
};
EventQueue.invokeLater(runner);
new ClassPathXmlApplicationContext("context.xml");
}
Here is the class Test where I want the injection :
#Component("test")
public class TEST{
#Resource(name="jpaSessionFactory")
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
#Transactional(readOnly = true)
public List<Personne> getPersonnes() {
Session s = sessionFactory.getCurrentSession();
Query query = s.createQuery("from Person");
return query.list();
}
}
Here is the server side from the application (not shown completely)
/*** The server side implementation of the RPC service.
*/
#SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
GreetingService {
#Resource
private TEST t;
public String greetServer(String input) throws IllegalArgumentException {
// Verify that the input is valid.
if (!FieldVerifier.isValidName(input)) {
// If the input is not valid, throw an IllegalArgumentException
back to
// the client.
throw new IllegalArgumentException(
"Name must be at least 4 characters long");
}
t.getPersons(); // NULL pointer here
.........................
The MySQL tables are created like it should, so all the scanning seems to work.
Thanks
Bob
This is because the GreetingServiceImpl class is not created by spring - it is servlet that is initialized by the container directly. Follow the instructions in this article to fix the issue. I have copied the relevant code from the article.
#Override
public void init() throws ServletException {
super.init();
final WebApplicationContext ctx =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
if (ctx == null) {
throw new IllegalStateException("No Spring web application context found");
}
ctx.getAutowireCapableBeanFactory().autowireBeanProperties(this,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
}

Spring 3.1 JPA not inserting data while running in tomcat

I have spent three days trying to find the solution to this problem to no avail. I am desperate to figure this out. I have a simple spring app, running in servlet 2.5 with jstl tags 1.2, running in tomcat with spring 3.1, using hibernate and the hibernate jpa implementation.
I can list data from a page, but I cannot complete an insert. The record goes back, it appears to fire through with no problems. Yet no insert takes place. I know there are other posts similar but I have looked through them all and have not been able to find a solution anywhere.
If I run the exact same code via a MAIN class, the insert works fine. It just does not work when running as a web-app in tomcat.
I have tried running this via the main which works, inside the controller I have skipped calling the service layer, trying to go directly to the interface, when that didnt work, I tried going directly to the implementing DAO class, and that didnt work. It appears via the spring logs, that the entity manager is getting created, and shut down before a transaction takes place.
Please help me.
Here is my App-context
<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:tx="http://www.springframework.org/schema/tx"
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/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"
default-autowire="byName">
<context:component-scan base-package="com.naturalbornliar.site"/>
<tx:annotation-driven />
<!-- Bean declarations go here-->
<bean id="duke" class="com.naturalbornliar.site.entity.Admin">
<constructor-arg name="admin_id" type="Long" value="15" />
<constructor-arg name="admin_login" type="String" value="testUser" />
<constructor-arg name="admin_pwd" type="String" value="testPwd" />
<constructor-arg name="email_id" type="int" value="15" />
<constructor-arg name="quote" type="String" value="Something to say here" />
</bean>
<!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> -->
<!-- <property name="driverClassName" value="com.mysql.jdbc.Driver"/> -->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/nbl_db"/> -->
<!-- <property name="username" value="web_user"/> -->
<!-- <property name="password" value="web_pwd"/> -->
<!-- <property name="initialSize" value="5"/> -->
<!-- <property name="maxActive" value="10"/> -->
<!-- </bean> -->
<bean id="simpledataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/nbl_db"/>
<property name="username" value="web_user"/>
<property name="password" value="web_pwd"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="simpledataSource"/>
</bean>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="nblPersistenceUnit"/>
<property name="dataSource" ref="simpledataSource"/>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
</bean>
<!-- <bean id="emf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> -->
<!-- <property name="persistenceUnitName" value="nblPersistenceUnit"/> -->
<!-- <property name="dataSource" ref="simpledataSource"/>-->
<!-- <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/> -->
<!-- </bean> -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf"/>
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL"/>
<property name="showSql" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
</bean>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor pointcut="execution(* *..CategoryDaoImpl.*(..))" advice-ref="txAdvice"/>
</aop:config>
</beans>
Here is my controller:
package com.naturalbornliar.site.mvc;
import javax.inject.Inject;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.naturalbornliar.site.entity.Category;
import com.naturalbornliar.site.entity.Link;
import com.naturalbornliar.site.service.CategoryService;
#Controller
#RequestMapping("/categories")
public class CategoryController {
protected final Logger logger = Logger.getLogger(CategoryController.class);
private final CategoryService categoryService;
#Inject
public CategoryController(CategoryService categoryService){
this.categoryService = categoryService;
}
#RequestMapping(value="/listCategories")
public String listLinks(Model model){
model.addAttribute("categories", categoryService.getAllCategories());
return "categories";
}
#RequestMapping(method=RequestMethod.GET, params="new")
public String showCreateCategoryForm(Model model){
model.addAttribute(new Category());
return "addcategory";
}
#RequestMapping(method=RequestMethod.POST)
public String addCategoryFromForm(Category category, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return"addcategory";
}
categoryService.addCategory(category);
return "redirect:/categories/listCategories";
}
}
Here is my service called from the controller:
package com.naturalbornliar.site.service;
import java.util.Collection;
import javax.inject.Inject;
import org.springframework.stereotype.Service;
import com.naturalbornliar.site.entity.Category;
import com.naturalbornliar.site.i.ICategoryDao;
#Service
public class CategoryService {
private ICategoryDao iCategoryDao;
#Inject
public CategoryService(ICategoryDao iCategoryDao){
this.iCategoryDao = iCategoryDao;
}
public Collection<Category> getAllCategories(){
return iCategoryDao.getAllCategories();
}
public Collection<Category> getCategoriesByType(String type) {
return iCategoryDao.getCategoriesByType(type);
}
public Category getCategoryById(Long id) {
throw new UnsupportedOperationException();
}
public void deleteCategory(Category category) {
throw new UnsupportedOperationException();
}
public void updateCategory(Category category) {
throw new UnsupportedOperationException();
}
public void inactivateCategory(Category category){
throw new UnsupportedOperationException();
}
public void addCategory(Category category){
iCategoryDao.addCategory(category);
}
}
Here is my implementing DAO:
package com.naturalbornliar.site.dao;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.naturalbornliar.site.entity.Content;
import com.naturalbornliar.site.i.IContentDao;
#Transactional
#Repository
public class ContentDaoImpl implements IContentDao {
protected final Logger logger = Logger.getLogger(ContentDaoImpl.class);
#PersistenceContext
private EntityManager em;
public EntityManager getEm() {
return em;
}
public void setEm(EntityManager em) {
this.em = em;
}
#Override
public void addContent(Content content) {
// TODO Auto-generated method stub
em.persist(content);
}
#Override
public void deleteContent(Content content) {
// TODO Auto-generated method stub
em.remove(content);
}
#Override
public void inactivateContent(Content content) {
// TODO Auto-generated method stub
}
#Override
public Content getContentById(Long id) {
// TODO Auto-generated method stub
return null;
}
#Override
public Content getContentByName(String name) {
// TODO Auto-generated method stub
return null;
}
#Override
public Collection<Content> getAllObjects() {
List<Content> resultList = em.createQuery("FROM Content", Content.class).getResultList();
return resultList;
}
}
Here is the interface (just in case)
package com.naturalbornliar.site.i;
import java.util.Collection;
import com.naturalbornliar.site.entity.Content;
public interface IContentDao {
public void addContent(Content content);
public void deleteContent(Content content);
public void inactivateContent(Content content);
public Content getContentById(Long id);
public Content getContentByName(String name);
public Collection<Content> getAllObjects();
}
Jeremy, thay may be many problems with that and I had similar problem.
In my case I used tomcat and I needed to add spring weaver to tomcat (this problem is described here: http://asrijaffar.blogspot.com/2007/02/spring-jpa-tomcat.html).
In my case I needed to have:
<tx:annotation-driven proxy-target-class="true" />
In DispatcherServlet config.
Additionally in db-context config:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="jpatest" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
Well I actually solved the problem. Here is the solution, there was another stackoverflow issue that solved it:
Spring #Transaction not starting transactions
Basically all that was needed for transactions to fire correctly was for me to add:
<tx:annotation-driven proxy-target-class="true"/>
to my servlet xml. I guess if you just have it in the application config.xml it only works when running under the main class (like in a standalone) if you need to run it in a container you have to declare transaction annotations in the servlet as well.
I also spent several hours trying to figure out where the problem is although my previous application with the same stack is working fine and I wasn't able to understand the difference.
And.... the error was in tag in spring-servlet.xml - it was defined to scan the root package with all the web controller, repository classes, etc.
After changing it to make scanning of the package with web controllers only, the problem was gone..
Just for you (and for me) in case you might meet the same problem, just an additional hint
I also had this same problem, spent couple nights searching - alex solutions saved me - in servlet xml I changed context:component-scan to scan only package with web controller.
Example from this page should look like
<context:component-scan base-package="com.naturalbornliar.site.mvc"/>

Resources