Importing Spring beans from other Maven modules inside a WAR? - java

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?

Related

Error in Bluemix Liberty when injecting a simple CDI managed bean into a servlet

When injecting a simple CDI managed bean into a servlet (starting from the generated code sample "SimpleServlet"), when accessing the servlet, Bluemix Liberty logs this error :
"SimpleServlet cannot be injected as class
wasdev.sample.servlet.SimpleServlet is not in a bean archive."
This is so even when a beans.xml file with discovery "all" is used.The target looks like this
Reason for Error :
The error is due to not having a beans.xml in the built application war i.e. JavaHelloWorldApp.war
Currently, for performance reasons, the beans.xml file is always required in order to use CDI on Bluemix.
Solution :
Copied the bean.xml inside the /src/main/webapp/WEB-INF/beans.xml then built the code.
This made sure that the bean.xml is packaged into the war file i.e. JavaHelloWorldApp.war and the error is resolved.
To verify bean.xml is deployed correctly to Bluemix, select the java application on Bluemix console, click and open the java application dashboard. Select Runtime, select Files tab, browse and verify that the bean.xml is deployed correctly inside WEB-INF as shown in the snapshot below.

BeanNotOfRequiredTypeException: spring factory not being resolved to child type

I'm having an issue with a PropertiesFactoryBean not being resulved to a Properties object in spring. Instead I get the following error:
org.springframework.beans.factory.BeanNotOfRequredTypeException: Bean named 'authProperties' must be of type [java.util.Properties], but was actually of type [org.springframework.beans.factory.config.PropertiesFactoryBean]
at org.springFramework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:367)
at org.springFrameowkr.beans.factory.support.AbstractBeanFactory.etBean(AbstractBeanFactory.java:198]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1121
at *our code here*
This code was written by others who have used it reliably for some time, so the issue is likely configuration issue rather then bad code. However, the failure occurs on the return line of this method:
private static Properties getPropsFromContext(String context) {
try(AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(context)){
return ctx.getBeans("authProperties", Properties.class));
}
}
The relevant lines of the XML file are:
<bean id="authProperties" class="org.springFramework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:props/auth.properties" />
</bean>
Ultimately the code is being called from within tomcat. We are using our own RealmBase class for the tomcat Relm, which calls this code. The jar containing our RealmBase class is in the tomcat/lib directory.
What should happen is that spring should recognize that PropertiesFactoryBean implements the FactoryBean interface and it should call getObject of the PropertiesFactoryBean as part of the getObjectForBeanInstance called by doGetBean method, which would return the Properties object. However, for some reason this is not happening, and I'm getting the PropertiesFactoryBean object back instead of the Property being returned. I've even looked through the code to verify this.
Our unit tests of this method run fine, which once again suggests the failure is not with the code, but sometime after it gets compiled, likely a configuration issue with tomcat. As far as I can tell there are no duplicate spring jars in the tomcat classpath or other obvious naming collisions.
I'm using Tomcat6. Our spring jars are mostly 3.2.9, a few 3.1.0, but I think the relevant jars are all 3.2.9
Why would it fail to generate the Property value correctly?
I ultimately discovered I was running an outdated version of the realm jar. When I installed the new Realm things worked correctly. I assume my issue was that the old Realm was compiled to a different version of spring.

Spring component scan for nested jar

Basically I have this Outer.jar, declared in it is an application context with a component scan:
<context:component-scan
base-package="x.y.z.class" />
However this x.y.z.class is in an inner.jar which is a dependency of outer.jar,
I'm getting an error that class not found .../Outer.jar/x/y/z/class , how can specify to check in the inner.jar?
UPDATE:
Initialize Application context as:
org.springframework.context.ApplicationContext ctx =
new ClassPathXmlApplicationContext("applicationContext.xml");
exception: I/O failure during classpath scanning; nested exception is java.io.FileNotFoundException: ..\default\deploy\test.war\WEB-INF\lib\inner.jar\x\y\z
and it says inner.jar/x/y/z/class not found
Outer.jar has inner.jar as the dependency
Make sure inner.jar (or whatever you name it to) exist on your classpath (either by adding to maven dependency, eclipse project settings, using -cp jvm command line arguments, etc), and just refer to the package name of the classes inside inner.jar you want to include.
Also make sure you don't get confused between jar, base package and fully-qualified class name. If I have a class com.mycoolcompany.service.Booya inside Blah.jar, typically I just need to do
<context:component-scan base-package="com.mycoolcompany.service.*" />
And ensure Blah.jar is on the parent project's classpath
If you are sure that the jar is under your classpath and still your classs is not autowired.
Are you sure that this classes are annotated properly??
Because what happens when you say component-scan, spring tries to find classes annotated with #Component or subtypes of compnenets like #Service, etc.
My guess is this is your third party dependency which might not have annotated classes.
In this case you should define beans manually in application context.
HTH

Annotation-specified bean name conflicts with existing, non-compatible bean definition

I'm using Spring 2.5.4 and am creating a Java application that I'm deploying onto Weblogic.
I have a class in an external library (which included in the WEB-INF/classes directory of the resulting WAR file of my application) that I want to use in my code. I've created an instance variable for an object of the class in my code and added the #Autowired annotation and a getter and setter. In my application context file I have declared a bean of the library class' type and added the following:
<context:annotation-config />
<context:component-scan base-package="com.mycompany" />
... in order to register an AutowiredAnnotationBeanPostProcessor that will scan the classes and process the annotation.
When I try and deploy the application, I get the following error:
java.lang.IllegalStateException: Annotation-specified bean name 'myBean' for bean
class [com.mycompany.package.ClassName] conflicts with existing, non-compatible
bean definition of same name and class [com.mycompany.otherPackage.ClassName]
I think this is because there's a class in the library which has the same name as one in my application code (both class' package names start with "com.mycompany"). Nb. this is NOT the class that I have added, but a different one. Is there any way I can circumvent this problem without changing the name of the class in my application?
Thanks for any assistance.
Old question but throwing my 2c of bad experience with similar problem.
If you have 2 classes with same name, but in different packages was there a time when you had your other class referenced by the failing Spring context? If so, I'd recommend to clean the AS cached files (typically the place where the WAR is extracted), clean/rebuild your WAR and deploy again. Restarting the app server is also recommended.
I found that application servers and web containers alike (Weblogic, WAS, Jboss, Tomcat) tend to leave behind the old classes and when application is deployed those stale .class files are loaded in JVM via some old references, which most of the time messes up the Spring context loader.
Typical scenario is when you have renamed/moved a class from one package to another, or even kept the package name the same but moved it to another module (jar). In such cases cached (left over) files in the AS work directory can cause big headaches. Wiping out the work directory in your AS should resolve the issue outright.
You should use #qualifier to avoid this kind of conflict please refer section 3.9.3.
I fixed the problem by removing the autowiring completely and accessing the bean by explicitly creating a reference to it through the application context and the getBean() method.
This would better fit as a comment to #Pavel Lechev's answer, but I don't have enough rep to comment yet.
For other's finding this, here's what I did to solve this problem. I am using Wildfly 9.0.2.Final and, IntelliJ IDEA 2016.1.3 Build #IU-145.1617. These steps should presumably work with JBoss as well.
Stop Wildfly server.
Navigate to $WILDFLY_HOME/standalone/. Delete the three following folders: lib/, log/ and temp/.
In IntelliJ, Build > Build Artifacts > All Artifacts > Clean (or just the artifacts you are deploying).
In IntelliJ, Build > Rebuild Project
Restart Wildfly and redeploy your artifact(s).
These steps remedied my issue of duplicate bean names detected in the Spring context after refactoring a package name upstream from a couple of Controllers.

Unable to import Spring bean definition file using relative path

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.

Categories

Resources