Unable to import Spring bean definition file using relative path - java

I am new to Spring and inherited a Spring project that had all the XML configuration in ProjectName/WebContent/WEB-INF/applicationContext.xml. I'm trying to break the configuration into different components so it is easier to substitute things like DataSources and Hibernate configuation when testing.
Here is my file structure:
ProjectName
->WebContent
->WEB-INF
->applicationContext.xml
->spring-datasource.xml
->spring-hibernate-properties.xml
->spring-persistence.xml
->test
->us.mn.k12... (Java pkgs with JUnit tests)
->spring-hsqldb-datasource.xml
->spring-test-bean-locations.xml
->spring-test-hibernate-properties.xml
->src
->us.mn.k12... (Java pkgs with production code)
In WEB-INF/applicationContext.xml, I import the following:
<import resource="spring-datasource.xml"/> <!-- Production datasource -->
<import resource="spring-hibernate-properties.xml"/> <!-- Production hibernate properties -->
<import resource="spring-persistence.xml"/> <!-- DAO's, hibernate .hbm.xml mapping files -->
The application works with the above configuration.
My JUnit tests run using DbUnit and an HSQLDB in-memory database. So my JUnit test references spring-test-bean-locations.xml, which has the following:
<import resource="spring-hsqldb-datasource.xml"/> <!-- HSQLDB datasource for test -->
<import resource="../WebContent/WEB-INF/spring-persistence.xml"/> <!-- Production DAO's, hibernate .hbm.xml mapping files -->
<import resource="spring-test-hibernate-properties.xml"/> <!-- Hibernate properties for test -->
In this way, I can specify test datasource and hibernate properties, but reuse the production mapping file for the DAO's, etc. However, I get an error running my JUnit test. Here is the relevant part of the exception:
Caused by: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from relative location [../WebContent/WEB-INF/spring-persistence.xml]
Offending resource: class path resource [spring-test-bean-locations.xml]; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [../WebContent/WEB-INF/spring-persistence.xml]; nested exception is java.io.FileNotFoundException: class path resource [../WebContent/WEB-INF/spring-persistence.xml] cannot be opened because it does not exist
Now if I move spring-persistence.xml into /test so that I don't have to use the relative path, and reference it with <import resource="spring-persistence.xml"/>, then the tests run fine. So I think the contents of my XML files are OK, but I'm not properly importing with a relative path.
Is there anything obvious I'm doing wrong with my import of the relative path? And maybe the bigger question is does this look like a reasonable strategy for breaking applicationContext.xml into components to make it easier for testing?
Thanks!

The problem is: anything inside WEB-INF is not available to the ClassLoader in a regular project setup (and spring uses the ClassLoader by default to access resources). There are some hacks to work around this (like referencing the contexts using the file: prefix), but those are mostly ugly.
A better practice I'd suggest is to move the context files out of WEB-INF and into a dedicated resource directory (src/main/resources if you have a maven setup). That way they will be available to both the webapp ClassLoader and local unit test ClassLoaders.
Read the resources chapter to further understand the mechanisms involved.

Use
<import resource="file:**/WebContent/WEB-INF/spring-persistence.xml" />
It works in spring 3.2.1.RELEASE. Old versions I am not sure.

Related

Tomcat deployment of application with HSQLDB database giving JDBCConnectionException

I'm having a struggle with my first attempt with little application based on Spring, Hibernate on HSQLDB and JSF, finally deployed with Tomcat. I face two problems now.
First of all I tried to run Java Application on console based on main (String args[]) method getting customerBo bean from spring-module.xml and insert/delete from embedded HSQLDB with Hibernate. Works like a charm.
1. Two configuration locations
Another step was a JSF page printing out a sample bean content. Working as well. However I had a struggle with resources and configuration:
resources: Here is all my configuration for database.
src/main/resources
\____ config
\____ database
\____ database.properties
\____ spring/beans
\____ data-source.xml
\____ hibernate-session-factory.xml
\____ spring-module.xml ... for Java Console Application
webapp: Here is all related to JSF including WEB-INF folder` related to web-page alike application.
src/main/webapp
\___ WEB-INF
\____ applicationContext.xml ... for web application
\____ faces-config.xml
\____ web.xml
\___ default.xhtml
How to make them easily communicate with each other? If web application starts from applicationContext in webapp, it needs to work with databse saved in resources. So it forces me to prefix all imports etc. with classpath such as.
<import resource="classpath:/spring-module.xml"/>
... or ...
<property name="location">
<value>classpath:/config/database/database.properties</value>
</property>
Java Console application still works well with these decors. Would you suggest me a better way? I import to applicationContext.xml all the database-stuff stored int spring-module.xml:
<beans xmlns=....>
<import resource="classpath:/spring-module.xml"/>
<import resource="classpath:/bean.xml"/>
<bean id="customerBo" class="nch.spring.customer.bo.impl.CustomerBoImpl"></bean>
</beans>
This is the URL I use to connect to HSQLDB
jdbc.url=jdbc:hsqldb:database/customers
2. HSQLDB on Tomcat
I deploy on Tomcat externally, not in IDE. I run localhost:8080.
After injecting all beans (working well, because I tested it on console), I recieved an error on Tomcat. Here is the shortened version with the first lines of causing:
org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Could not open connection
Caused by: org.hibernate.exception.JDBCConnectionException: Could not open connection
Caused by: java.sql.SQLException: No suitable driver found for jdbc:hsqldb:database/customers
My database location related to project. It's included in the WAR:
project
\____ src/main/java
\____ src/main/resources
\____ src/main/webapp
\____ database
\____ customers
\____ customers.script
\____ customers.lck
\____ customers.properties
And my pom.xml for HSQLDB:
<!-- HSQLDB -->
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.2</version>
</dependency>
Why I cannot print out the database content with Spring and Hibernate on JSF deployed on Tomcat, but I can print it out on console? Is there an another way to embed a database? Does Tomcat support HSQLDB at all? I have tried to work first with MySQL, but it had been so cumbersome for me.
Shortly:
HSQLDB works with Spring and Hibernate well, proved on Java Console Application.
JSF pages working as well, able to print out the content of any bean.
My application refuses to communicate with database since deployed on Tomcat. Here is my full source code on GitHub.
The core of the issue is that you're using HSQLDB with a database file, that is actually a resource of your project (i.e. inside its classpath). This means that the file will be embedded inside your WAR, and you will not be able to update it (as contents inside the WAR file will be read-only).
Your connection string for HSQLDB is:
jdbc.url=jdbc:hsqldb:database/customers
When ran inside your IDE, you need to be aware that it does not package your application in a JAR but as an exploded directory. So when you run it as a console application, it will access the database file and update it just fine. However, when packaged in a WAR, it won't find it.
If your intent is to do read-only on the database, you could configure HSQLDB with a Resource Database URL
of the form
jdbc:hsqldb:res:/database/customers
res: stored in a Java resource, such as a Jar and always read-only.
This will load a database from the resource located in /database/customers of the classpath of your application.
However, if you want to update it, then you have to use another way. A typical way is to use a Server Database URL, where the database is hosted on a server, possibly localhost for testing purposes.

Resolve Spring injections for dependant jar in base Project

I am working on a project where we developed various jars as small modules. Then these jars were added as dependencies in a base project.
In the base projects applicationContext we have imported the applicationContext of all dependendant jars using the following line
<import resource="classpath*:applicationContext.xml" />
The above line makes sure that all the applicationContexts.xml s get loaded. On code analysis I found that each of the module in its applicationContext has the dataSource injected. Although all the jars are going to use the same datasource still each of the jar is instantiating its own datasource. Is there a way by which I can specify a global datasource which can be injected through base class itself.
In each of the jars we have a datasource injected which the jar uses to perform database operations. I would want the jars to use a datasource of base jar instead of its own datsources.

Embedded GlassFish ignores Maven test resources

I have several session beans that I have written unit tests for. I have setup Maven to include a persistence.xml in the src/main/resources/META-INF directory that refers to a local MySQL database for development purposes. I have another persistence.xml in src/test/resources/META-INF directory that refers to the embedded Derby database __default. The tests are deployed to an embedded GlassFish 3.1 container.
When I run the tests however, I get the following error:
java.lang.RuntimeException: javax.naming.NamingException: Lookup failed for 'jdbc/mylog'
jdbc/mylog is the MySQL database that the persistence unit in the main directory refers to. It is obviously ignoring the persistence unit in the test directory but I have no clue as to why.
Maven is setting the classpath correctly as far as I can tell, with test-classes before classes and a peek in the actual target/test-classes/META-INF directory reveals that it copied the correct, embedded Derby, persistence unit.
[DEBUG] Test Classpath :
[DEBUG] C:\Users\Laurens\Documents\Projects\Mylog\target\test-classes
[DEBUG] C:\Users\Laurens\Documents\Projects\Mylog\target\classes
[DEBUG] C:\Users\Laurens\.m2\repository\org\eclipse\persistence\eclipselink\2.2.0\eclipselink-2.2.0.jar
[DEBUG] C:\Users\Laurens\.m2\repository\org\eclipse\persistence\javax.persistence\2.0.3\javax.persistence-2.0.3.jar
[DEBUG] C:\Users\Laurens\.m2\repository\org\eclipse\persistence\org.eclipse.persistence.jpa.modelgen.processor\2.2.0\org.eclipse.persistence.jpa.modelgen.processor-2.2.0.jar
[DEBUG] C:\Users\Laurens\.m2\repository\org\glassfish\extras\glassfish-embedded-all\3.1\glassfish-embedded-all-3.1.jar
[DEBUG] C:\Users\Laurens\.m2\repository\javax\javaee-web-api\6.0\javaee-web-api-6.0.jar
[DEBUG] C:\Users\Laurens\.m2\repository\junit\junit\4.8.1\junit-4.8.1.jar
Any hint on how to have GlassFish use the proper persistence unit very much appreciated! Thanks!
When running tests using embedded Glassfish, the JPA provider does not use the classpath displayed on the command-line, before executing the maven-surefire-plugin goal (that is used to run the test phase). Embedded Glassfish deploys the artifacts that are available as part of a test scope, as a ScatteredArchive. This scattered archive is typically created in the java.io.tmpdir directory usually with the name gfembed<a_random_number>tmp, unless the embedded Glassfish configuration specified the location of a Glassfish installation root, and a Glassfish domain.
When the embedded Glassfish domain is prepared with the deployed scattered archive, the files to be deployed are typically copied into an exploded directory that houses all the classes (including all dependencies) required by the application. This directory typically happens to be present in the GF_EMBED_DOMAIN_HOME/applications/<application_name> directory. The persistence.xml files from your src/main/resources/META-INF and src/test/resources/META-INF directories are copied here into the <application-name>/META-INF directory. Needless to state, the one that gets copied last, or the one that doesn't get overwritten is the one that is used by the JPA provider during the tests. This always happens to be the file in src/main/resources/META-INF.
You can overcome this situation in two ways:
1. Using a custom Glassfish domain configuration file
You can specify a domain configuration file (domain.xml) that will contain the datasource definition for jdbc/mylog. This is what I do currently for it is very flexible and the domain configuration file can contain other configurations as well. The config file, needs to specified as part of test setup in the following way:
Map<String, Object> props = new HashMap<String, Object>();
props.put("org.glassfish.ejb.embedded.glassfish.installation.root", "./glassfish-install/glassfish");
container = EJBContainer.createEJBContainer(props);
context = container.getContext();
datasource = (DataSource) context.lookup("jdbc/mylog"); //You can lookup the datasource too, to confirm that your setup is successful.
The afore-mentioned glassfish-install directory and its sub-directory glassfish are present in the Maven project root (and also checked into version control); the glassfish directory must contain a directory structure of domain1/config to represent the directory structure of the Glassfish domain of name domain1. The structure in the project can be seen in the below screenshot. The other related files (the JDBC resource adapter JARs and the like), can be obtained from a Glassfish installation directory, but typically these might also be placed in the correct location by the embedded Glassfish runtime, if configured correctly.
The contents of the Glassfish domain configuration file are different from the default one used by embedded Glassfish, except for the datasource and connection pool configuration (the relevant entries added in my usecase where I perform integration tests, have been posted below):
<domain log-root="${com.sun.aas.instanceRoot}/logs" application-root="${com.sun.aas.instanceRoot}/applications" version="10.0">
<system-applications/>
<applications/>
<resources>
<jdbc-resource pool-name="MyPool" jndi-name="jdbc/mylog"/>
...
<jdbc-connection-pool driver-classname="" datasource-classname="org.apache.derby.jdbc.ClientDataSource" res-type="javax.sql.DataSource" description="" name="MyPool" ping="true">
<property name="User" value="APP"></property>
<property name="RetrieveMessageText" value="true"></property>
<property name="CreateDatabase" value="true"></property>
<property name="ServerName" value="localhost"></property>
<property name="Ssl" value="off"></property>
<property name="SecurityMechanism" value="4"></property>
<property name="TraceFileAppend" value="false"></property>
<property name="TraceLevel" value="-1"></property>
<property name="PortNumber" value="1527"></property>
<property name="LoginTimeout" value="0"></property>
<property name="Password" value="APP"></property>
<property name="databaseName" value="MYDB"></property>
</jdbc-connection-pool>
...
</resources>
<servers>
<server name="server" config-ref="server-config">
<resource-ref ref="jdbc/__TimerPool"/>
<resource-ref ref="jdbc/__default"/>
<resource-ref ref="jdbc/mylog"/>
</server>
</servers>
...
...
The default domain.xml file can be downloaded from the java.net site, and modified, in the event you wish to keep the changes as minimal as possible, instead of copying one from a Glassfish installation.
2. Copying over the persistence.xml files
One can add goals to the Maven POM file, to backup and copy the persistence.xml file from src/test/resources/META-INF to src/main/resources/META-INF, before the test phase. After the test phase is complete, the original is restored. I will not go into details of this, as a similar solution is already discussed in a related StackOverflow question. I did not use this approach for integration tests as I required changes to be done beyond the ones that can be carried in persistence.xml, like creation of a custom realm. I use it for unit-tests however, due to the fact that the JPA provider will fetch the persistence.xml file from target/classes instead of target/test-classes, despite the latter appearing first in the classpath order. If you use Hibernate as your JPA provider, enabling TRACE logging for the org.hibernate.ejb logger (as the Ejb3Configuration class is responsible for performing the lookup) would convince you that file in test-classes will not be picked up.
Note:
Most of the answer assumes Glassfish 3.1 but may hold good for upcoming versions as well.
By "embedded glassfish container", do you mean a maven plugin that runs glassfish for you? The classpath for a maven plugin is different and managed differently than the maven test classpath. You might need to be working with a different classpath.
This answer might sounds silly but I was looking for a way which lets me run those tests from eclipse by Run As -> JUnit Test. This is how I made it:
#BeforeClass
public static void setUp() throws IOException {
Files.copy(new File("target/test-classes/META-INF/persistence.xml"), new File("target/classes/META-INF/persistence.xml"));
// ...
}
I'm just copying the test/persistence.xml to classes/persistence.xml. This works.

Divide Spring configuration across multiple projects

we have quite a few projects that use the same codebase (backend code).
Just the frontend tends to be different.
We decided that the best approach would be to seperate backend and frontend into different projects: Engine and Project_name
Now these are Spring-projects. So it would only seem logical if we divide the Spring configurations aswell:
Database.xml, Services.xml would belong to the project Engine.
And a specific Frontend.xml would belong to Project_Name.
To link these up, I would need a generic SpringBeans.xml that imports all of these XML's.
I tried following directory structure:
Engine Project
Config
Spring
Database.xml
Services.xml
Project_Name Project
Config
SpringBeans.xml
spring
Frontend.xml
The contents of SpringBeans.xml are simply:
<import resource="spring/Database.xml"/>
<import resource="spring/Services.xml"/>
<import resource="spring/Frontend.xml"/>
I set up Eclipse so the Project_Name project references the Engine project.
When I start it, SpringBeans.xml gets found, however the XML files in the Engine project aren't found (FileNotFoundException).
I'll also note that before splitting up the Engine and Frontend code into different projects, the principle of importing other XML files worked like a charm.
So my question to you: Is it possible to make different Spring configrations in different projects play together nicely?
Since the spring bean configuration files are in the classpath, you need to add the prefix claspath to the resource location:
<import resource="classpath:spring/Database.xml"/>
<import resource="classpath:spring/Services.xml"/>
<import resource="classpath:spring/Frontend.xml"/>

Importing Spring beans from other Maven modules inside a WAR?

I have a new web app that is packaged as a WAR as part of a multi-module Maven project. The applicationContext.xml for this WAR references beans that are imported from the "service" module, which in turn imports beans from the "dao" module. The import statement in applicationContext.xml looks like this:
<import resource="classpath*:service.xml" />
and the one inside the service.xml file looks like this:
<import resource="classpath*:dao.xml" />
Neither Spring STS, nor Eclipse show any warnings or errors in my bean files. I reference the imported beans all over the place. The Maven build works fine and the DAO integration tests all pass (they use the beans). I don't have any service integration tests yet.
But when I start up the WAR in Jetty I get an error:
Error creating bean with name 'securityService'
Cannot resolve reference to bean 'userDAO' while setting constructor argument
All of the imported bean XML files can be found inside their respective JAR files in the WEB-INF/lib directory. Indeed, the service bean that threw the error is itself defined inside the service.xml file inside the service module's JAR file.
Apparently the service module can't find the bean that it imported from the dao module. Obviously I don't understand something...seems like this should this Just Work?
I enabled DEBUG logging for 'org.springframework' in order to see if I could learn anything. What I found were messages to the effect that the DAO beans had been created, but there was also a message about them having no name or id.
I check the file, and they all did have an id. So what was it? I check the XML namespace and saw:
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
and noticed it was old (I am using Spring 3.0.2) and changed it to:
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
Once I changed it, Spring instantly threw half a dozen errors regarding beans that were defined incorrectly (but never used apparently). Once I fixed those errors, everything Just Worked. I've since gone through the entire system checking Spring XML file namespace versions.
Thanks to all for the help. Can't believe I wasted a day on this stupidity!!
The difference between the classpath:thingy.xml and classpath*:thingy.xml notation is that the former uses the standard classpath mechanism to resolve one resource (using ClassLoader.getResource(name)), whereas the latter will use ClassLoader.getResources(name) to retrieve all matching resources on the classpath, a distinction that should be irrelevant in your situation as I guess there is only one dao.xml file on the class path.
I think your problem is different, you are missing a leading slash.
Use this for a single resource
<import resource="classpath:/dao.xml" />
and this for multiple resources
<import resource="classpath*:/dao.xml" />
See
Spring Reference: The classpath*
prefix
Sun JavaDocs: ClassLoader
It should be like
<import resource="classpath:service.xml"/>
Are you having multiple applicationContexts and possibly the parent context is referring to a bean defined in the child context?

Categories

Resources