I'm trying to implement a group of tests for my app. In this case, I have some mybatis mappers whose beans are defined in my applicationContext.xml. For example:
<bean id="usersMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.myapp.dao.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
I've been looking for hours how to implement junit tests properly because some internet posts are deprecated or not up to date. This is my junit class actually:
#ContextConfiguration(locations = {"classpath:*applicationContext.xml"})
#RunWith(SpringJUnit4ClassRunner.class)
public class GroupTest {
#Autowired
ApplicationContext context;
#Test
public void testCreateGroup() throws SQLException {
UserMapper um = (UserMapper)context.getBean("usersMapper");
}
}
There are no errors during the startup. When I try to get the bean usersMapper returns an exception (There is no bean definition..) Maybe, is not loading the properly applicationContext?
I also tried Mockito with no success. I've read it does cool things, but is it capable of loading the context as well as Spring? When I call the getUsers method from UserMapper, it returns null. This is my implementation:
#ContextConfiguration(locations = {"classpath:*applicationContext.xml"})
#RunWith(SpringJUnit4ClassRunner.class)
public class GroupTest {
#Mock
private UserMapper userMapper;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testCreateGroup() throws SQLException {
userMapper.getUsers();
}
}
For the record... my applicationContext.xml is placed in /src/main/webapp/WEB-INF/applicationContext.xml
Hope you can guide me the right way. Thank you
Edit1: applicationContext.xml and context.xml added:
<?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:p="http://www.springframework.org/schema/p"
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">
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/adminDB"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="/WEB-INF/mybatis-config.xml" />
</bean>
<bean id="usersMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="es.unican.meteo.dao.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
</beans>
It seems that Peter Hart solution loads the applicationContext.xml but a new problem appears. I need to use a jndi Resource in my app (reference included in applicationContext.xml). Is this no possible in test environment?
The exception shows the following:
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
This is my context.xml
<Context>
<Resource name="jdbc/adminDB" auth="Container" type="javax.sql.DataSource"
maxActive="20" maxIdle="10" username="***" password="***"
driverClassName="org.apache.derby.jdbc.ClientDriver"
url="jdbc:***"/>
</Context>
The problem is that you've requested a classpath resource, and WEB-INF/applicationContext.xml is probably not on the classpath (it usually wouldn't be).
You could try moving the applicationContext.xml to somewhere on the classpath. I'm guessing that you are using maven, in which case, this would usually either be src/main/resources, or src/test/resources (if this is specific to a particular test, rather than a 'real' application context file). I would also fix your #ContextConfiguration as #ohiocowboy suggests (assuming you put it directly in that directory, as things will generally work better if you're explicit about the location).
If your application context absolutely needs to be in WEB-INF/applicationContext.xml, you might try using file:src/main/webapp/WEB-INF/applicationContext.xml. If you run from the base directory of the project with mvn test, it should work, and it would probably work from the eclipse test runner also (no idea about Idea or NetBeans, but my guess is that it might work).
Your application context is not being loaded properly. The locations attribute of the #ContextConfiguration annotation should be "classpath:main/webapp/WEB-INF/applicationContext.xml", "classpath:**/applicationContext.xml" or "classpath*:applicationContext.xml". When you run the test, the application is not deployed, hence WEB-INF will not be in the classpath(unless you have added it to the test classpath yourself).
When using a mock object, you need to provide mock implementations for the methods that are going to be called. The getUsers() method is returning null because you have not set the desired result. You can check the mockito website for examples at http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html.
try
#ContextConfiguration(locations = {"classpath:/applicationContext.xml"})
UPDATE
Actually that doesn't work either because. The applicationContext.xml isn't on the classpath. It needs to be moved into the WEB-INF/classes directory.
Related
Premise
I have a Spring 5.1.5 project with Hibernate 5.4.1
The compilation goes through fine but while running test cases for a particular package I see multiple tests failing. All with the same error:
javax.persistence.TransactionRequiredException: no transaction is in progress at com.project.server.package.dao.impl.SomeDAOImplTest.someTest(SomeDAOImplTest.java:54)
The Problem
Now I know that Hibernate 5 enforces the check for a transaction and it doesn't find one here and throws an exception. My question is why does it do that given I have transactions initialized via spring context.
My test case:
#ContextConfiguration({ "classpath:/spring/applicationContext-package-dao--test.xml" })
public class SomeDAOImplTest extends AbstractDAOTest {
#Autowired
private SomeDAO someDAO;
private className obj;
#Before
public void setUp() {
obj = new ClassName();
obj.setId(3);
someDAO.saveOrUpdate(obj);
}
My applicationContext-package-dao--test.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:/spring/applicationContext-persistence-datasource-test.xml" />
<import resource="classpath:/spring/applicationContext-package-dao.xml" />
</beans>
The imported context applicationContext-persistence-datasource-test.xml has a bean txProxyTemplate as below:
<beans>
.
.//other beans
.
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="transactionManager" class="com.desktone.transaction.DtResourcelessTransactionManager" />
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
.
.//other beans
.
</beans>
The other applicationContext-package-dao.xml has the bean definition for SomeDAO which has txProxyTemplate as it's parent.
<bean id="SomeDAO" parent="txProxyTemplate">
<property name="target">
<bean class="com.project.server.package.dao.impl.SomeDAOImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="saveOrUpdate">PROPAGATION_REQUIRED</prop>
<prop key="delete">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
The saveorupdate call from SomeDAOImplTest calls SomeDAOImpl whose spring context config is applicationContext-package-dao.xml and has the Hibernate saveorupdate() call.
Things I have Tried:
Manually adding #Transactional tag to SomeDAOImplTest. (still throws no tx error)
Experimenting with PROPAGATION.REQUIRES_NEW (still throws no tx error) & PROPAGATION.MANDATORY(says marked mandatory but no tx).
Made sure the autowire is initialzing the bean.
Primary Suspicion
I suspect somehow the someDAO bean is initialized but txProxyTemplate bean isn't, so no transactionManager is in place. However, I have found no clues to coroborate this.
For me, this was happening because of a very fundamental issue. I'll explain the issue and the fix will be intuitive to everyone afterward.
When a Spring application runs all the beans are loaded in a single/global context. So even if some spring bean configuration depends on a transaction bean(for me it was txProxyTemplate) which isn't present in the same package at runtime it will be able to access it.
However, that's not true for a test case. My tests were reporting no transaction in progress since they couldn't load the txProxyTemplate and actually start a transaction. So my tests never ran in a transaction and I didn't know any better until I upgraded to Hibernate 5 and they put a hard constraint on this.
As you might have guessed defining the txProxyTemplate in the same spring config helped me work past this issue.
Good learning.
I am working on cache implementation (exstremescale)for maven multi module project, where i have added below maven dependency
<dependency>
<groupId>com.ibm.extremescale</groupId>
<artifactId>ogclient</artifactId>
<version>8.6.0.20150901-215917</version>
</dependency>
Added caching annotation on
#Override
#Cacheable(value = "productDetails", key = "#productId + #orgId")
public Product productRead(final String productId, final String productKey, final String orgId, final CRApplicationEnum sourceSystem) throws IntegrationException {
cache-manager.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:p="http://www.springframework.org/schema/p"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager" primary="true">
<property name="caches">
<set>
<bean class="com.ibm.websphere.objectgrid.spring.ObjectGridCache"
p:name="eventDetails" p:map-name="${iev.eventDetails.mapName}"
p:object-grid-client-ref="wxsGridClient" />
<bean class="com.ibm.websphere.objectgrid.spring.ObjectGridCache"
p:name="eventValidationDetails" p:map-name="${iev.eventValidationDetails.mapName}"
p:object-grid-client-ref="wxsGridClient" />
<bean class="com.ibm.websphere.objectgrid.spring.ObjectGridCache"
p:name="productDetails" p:map-name="${ipr.productDetails.mapName}"
p:object-grid-client-ref="wxsGridClient" />
</set>
</property>
</bean>
<bean id="wxsCSDomain"
class="com.ibm.websphere.objectgrid.spring.ObjectGridCatalogServiceDomainBean"
p:catalog-service-endpoints="${xscale.catalogServiceEndpoint}" />
<bean id="wxsGridClient"
class="com.ibm.websphere.objectgrid.spring.ObjectGridClientBean"
p:catalog-service-domain-ref="wxsCSDomain" p:objectGridName="${wxs.objectGridName}" />
Caching is working for only one maven module of the project, i can see the cache interceptor call and for rest of the maven module it is ignoring the #cacheable annotation(it is not going to the interceptor).
We dont have PostConstructor or Self invokation
We are using atomikos as transaction manager and CXF -interceptors which will be executed before coming to caching methods.
Please help me on this
Your comment about JdkDynamixAopProxy and looking at the code makes me think that the method you have annotated with #Cacheable is in a concrete class. And for the annotation on a concrete class to exhibit proper behavior; you need to enable the cglib proxying in your application.
This can be done by adding proxy target class parameter to your cache annotation driven tag.
<cache:annotation-driven proxy-target-class="true"/>
If you dont want to enable class based proxying for your overall application; you can specify the behavior for a particular class by annotating it with this annotation:
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
Calling methods in the same class bypasses the dynamic proxy and any cross cutting concern like caching, transaction etc which is part of the dynamic proxies logic is also bypassed. So could your problem be Spring cache #Cacheable method ignored when called from within the same class ?
If so, the fix is to use AspectJ compile time or load time weaving.
spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="meassageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="resource\message">
</property>
</bean>
</beans>
Main.java class file
public class Main {
public static void main(String[] args) {
ApplicationContext context= new ClassPathXmlApplicationContext("spring.xml");
System.out.println(context.getMessage("emp", null, Locale.US));
}
}
My properties file is in src/resource folder. File name is mesaage_en_US.properties.
I have also tried with different file names like message.property, message_en.property and with different locales like Locale.English, Locale.UK but no luck.
I moved the property file to src folder but getting same exception.
I am getting following exception.
Exception in thread "main" org.springframework.context.NoSuchMessageException: No message found under code 'emp' for locale 'en_US'.
at org.springframework.context.support.DelegatingMessageSource.getMessage(DelegatingMessageSource.java:65)
at org.springframework.context.support.AbstractApplicationContext.getMessage(AbstractApplicationContext.java:1234)
at org.beans.Main.main(Main.java:14)
Please help.
message_en_US.properties
emp=Hello Employee.
I like to use a PropertySourcesPlaceholderConfigurer for that. Here's a great tutorial to get you started.
Basically, you'll want to add:
<context:property-placeholder location="classpath:foo.properties" />
to your spring xml config file, where "foo.properties" is a resource's absolute path within the class path.
Then you can inject them into fields like this:
#Value( "${jdbc.url}" )
private String jdbcUrl;
where "jdbc.url" is the reference name in your properties file.
Of course, the #Value won't work inside your static void main, but I really doubt static void main is where you want to use your properties anyway. You ought to be accessing them from a Spring Bean.
I think this is duplicated from this question. Basically, it has to do with a mismatch between your bundle and the locale specified in code.
Instead of getting message from ApllicationContext I am getting message from MeassageSource itself. I changed my spring.xml like this
<bean id="employee" class="org.bean.Employee" >
<property name="id" value="1"/>
<property name="name" value=""/>
<property name="dept" value=""/>
<property name="messages" ref="messageSource"/>
</bean>
Now I am calling messages.getMessage(this.messages.getMessage("emp", null, Locale.UK)) from Employee class. Its working.
Change to
<property name="basename" value="message" />
with message_en_US.properties in the same folder as your spring.xml.
EDIT : You have a typo in your bean name when defining the MessageSource. It's name should have been exactly messageSource. Because of that extra a in meassageSource ApplicationContext failed to load it.
I've reproduced your error and found the problem. It has to do with Spring not finding the bundle. I think you should be getting a warning before the exception with the following message:
WARNING: ResourceBundle [resource\message] not found for MessageSource: Can't find bundle for base name resource\message, locale en_US
This has been the hint. The problem is related to your project structure and how the bundles are searched when specifying setBasename property. Please take a look at this.
Anyway I think you should put your bundles in the more standard location src/main/resources. If you follow this convention, your messageSource bean should be defined like this:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="message" />
</bean>
With this approach your example should produce the desired line:
Hello Employee.
I am attempting to use Apache Shiro with Spring and MongoDB. I am using Spring Data Repositories which are autowired. I have created my own custom realm for Shiro which uses a Spring Data repository to talk to Mongo:
public class PlatformRealm extends AuthorizingRealm {
#Autowired(required = true)
protected UserRepository userRepository = null;
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
...
}
}
The problem I'm seeing is the userRepository isn't being autowired. I get the following line in my console output referring to the PlatformRealm:
INFO org.springframework.web.context.support.XmlWebApplicationContext - Bean 'platformRealm' of type [class com.resonance.platform.core.security.PlatformRealm] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
This is because of the Apache Shiro ShiroFilterFactoryBean. What is happening is this bean and all of its dependencies are being loaded up immediately when the container is started. It doesn't wait for my persistence beans to be initialized prior to resolving dependencies. This causes the repository reference to be null.
The following bean configurations are loaded via the contextConfigLocation parameter:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/web-platform-persistence.xml,
/WEB-INF/web-platform-services.xml
</param-value>
</context-param>
Services bean configuration:
<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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean id="userSession"
class="com.resonance.platform.web.core.services.ShiroUserSessionService" />
<!-- Shiro (Security) -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/" />
<!-- The 'filters' property is not necessary since any declared javax.servlet.Filter
bean -->
<!-- defined will be automatically acquired and available via its beanName
in chain -->
<!-- definitions, but you can perform instance overrides or name aliases
here if you like: -->
<!-- <property name="filters"> <util:map> <entry key="anAlias" value-ref="someFilter"/>
</util:map> </property> -->
<property name="filterChainDefinitions">
<value>
# some example chain definitions:
/admin/** = passThruFilter, roles[admin]
/** = passThruFilter
</value>
</property>
</bean>
<bean id="passThruFilter"
class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter" />
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- Single realm app. If you have multiple realms, use the 'realms' property
instead. -->
<property name="realm" ref="platformRealm" />
<!-- By default the servlet container sessions will be used. Uncomment
this line to use shiro's native sessions (see the JavaDoc for more): -->
<!-- <property name="sessionMode" value="native"/> -->
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" />
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- Define the Shiro Realm implementation you want to use to connect to
your back-end -->
<!-- security datasource: -->
<bean id="platformRealm" class="com.resonance.platform.core.security.PlatformRealm" />
Persistence bean config:
<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:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<mongo:mongo id="mongo" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongo" />
<constructor-arg value="platform" />
<property name="writeConcern">
<util:constant static-field="com.mongodb.WriteConcern.SAFE" ></util:constant>
</property>
</bean>
<mongo:repositories base-package="com.resonance.platform.core.data.repositories" />
User Repository:
package com.resonance.platform.core.data.repositories;
import org.bson.types.ObjectId;
import org.springframework.data.repository.CrudRepository;
import com.resonance.platform.core.entities.User;
/**
* A repository used to manage User entities.
* #author Kyle
*/
public interface UserRepository extends CrudRepository<User, ObjectId> {
/**
* Gets a user by the specified login.
* #param login
* #return
*/
User getByLogin(String login);
}
My question is, how can I get the userRepository dependency to resolved properly? I understand that the ShiroFilterFactoryBean has to be initialized before the other dependencies and whatnot, but there must be a way to get the userRepository dependency to be resolved.
EDIT: Added User Repository code.
I am running into the same problem described here.
I am noticing two spring factories.
from the dispacher-servlet.xml which loads #Service #Repository classes due to component-scan defined at an base package level so I can #Autowire Service class into Controller.
from application context doesn't seem to #Autowire classes marked as #Service because they are not loaded.
If I understand you right you should be able to create a subclass of ShiroFilterFactoryBean which implements org.springframework.beans.factory.InitializingBean. In InitializingBean.afterPropertiesSet() you would then add some code that gets the UserRepository and sets it to that field. Not the most elegant solution, but this looks like an exceptional case.
I've had this problem too. It has something to do with the order of bean initialization in the Spring container. The workaround is not to autowire the repository but have your realm implement ApplicationContextAware and get the needed beans straight from the context. It's not elegant, but it'll work.
I am not too sure if this is helpful, but you may check this question from me for an alternative solution.
But, the core issue probably still stays open.
Concrete problem explanation taken from ShiroFilterFactoryBean-and-a-spring-data-mongodb-realm:
The problem is that spring-data-mongodb requires a spring
ApplicationEventMulticaster to have been initialised before it can be
used.
ShiroFilterFactoryBean is a beanPostProcessor, and as such, during
initialisation, spring attempts to configure its realms(and hence my
realm and spring data mongo based userDao). it fails because
ApplicationEventMulticaster has not yet been created.
After I've tried several suggested ways to solve this problem, like the InitializingBean, ApplicationContextAware or BeanPostProcessor interfaces (each resulting in a premature invocation, hence before initializing my necessary service/repository stuff), I came up with the following solution:
Let spring create your shiro context without any automatic bean resolution to your services/repositories.
Let spring create your service/repository context, including mongodb
Create a simple class which will take care of your shiro-service coupling and configure it accordingly in your spring config. This class will be invoked after your shiro and service context has been successful set up.
To (1), sth. like this:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="filterChainDefinitions">
<value>
<!-- Your definitions -->
</value>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"
p:realm-ref="myShiroRealm" />
<bean id="myShiroRealm" class="com.acme.MyShiroRealm"
<!--no bean refs here-->
/>
To (2), sth. like this:
<bean id="myService" class="com.acme.MyService"
c:myRepository-ref="myRepository" />
...
<!-- Ask Spring Data to scan our repositories -->
<mongo:repositories base-package="com.acme.repository.impl.mongodb" />
To (3):
public class ShiroRealmServiceBridge {
public static void postInject( MyShiroServerRealm realm, MyService service ) {
realm.setService( service );
}
}
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass"><value>com.acme.ShiroRealmServiceBridge</value></property>
<property name="targetMethod"><value>postInject</value></property>
<property name="arguments">
<list>
<ref bean="myShiroRealm" />
<ref bean="myService" />
</list>
</property>
Advantages:
It works xD
No additional burden/dependencies on your shiro stuff
Complete spring configuration and setup, resulting in a consistent state after initialization
Disadvantage:
One time overhead setup
May result in an inconsistent state, which will complain at runtime rather than at startup, if you forget or bump the glue-configuration
The ShiroFilterFactoryBean implements the BeanPostProcessor and, since it has dependencies on the security manager w/ its own dependencies on data stores, data access objects, etc. it can cause a whole slew of Bean X of type Y is not eligible for getting processed by all BeanPostProcessors messages.
The worst part is that it seems to be just a way to see the Filter implementations that Spring is instantiating in order to track and possibly inject properties into AuthorizationFilters.
Frankly I don't need that headache just for filter tracking, so I created a custom version that did not include the BeanPostProcessor. I'm now forced to manually wire in Filter implementations to the beans "filters" property, but at least I don't have to deal with that error and the questionable status of my security manager and related beans.
Does somebody know how to integrate the cxf framework without using the cxf plugin? I have already published a simple service, but my problem is to inject existing grails service bean in the cxf jaxws bean.
In applicationContext.xml i'm using following definition
<jaxws:server id="jaxwsService" serviceClass="at.pdts.cxf.HelloWorld" address="/hello">
<jaxws:serviceBean>
<bean class="at.pdts.cxf.HelloWorldImpl">
<property name="halloService"><ref bean="helloWorld"></ref></property>
</bean>
</jaxws:serviceBean>
</jaxws:server>
The helloWorld bean is a normal grails serivce class. During startup i get following exception.
Cannot resolve reference to bean 'helloWorld' while setting bean
property 'halloService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'helloWorld' is defined
applicationContext.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:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
<bean id="grailsApplication" class="org.codehaus.groovy.grails.commons.GrailsApplicationFactoryBean">
<description>Grails application factory bean</description>
<property name="grailsDescriptor" value="/WEB-INF/grails.xml" />
<property name="grailsResourceLoader" ref="grailsResourceLoader" />
</bean>
<bean id="pluginManager" class="org.codehaus.groovy.grails.plugins.GrailsPluginManagerFactoryBean">
<description>A bean that manages Grails plugins</description>
<property name="grailsDescriptor" value="/WEB-INF/grails.xml" />
<property name="application" ref="grailsApplication" />
</bean>
<bean id="grailsConfigurator" class="org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator">
<constructor-arg>
<ref bean="grailsApplication" />
</constructor-arg>
<property name="pluginManager" ref="pluginManager" />
</bean>
<bean id="grailsResourceLoader" class="org.codehaus.groovy.grails.commons.GrailsResourceLoaderFactoryBean">
<property name="grailsResourceHolder" ref="grailsResourceHolder" />
</bean>
<bean id="grailsResourceHolder" scope="prototype" class="org.codehaus.groovy.grails.commons.spring.GrailsResourceHolder">
<property name="resources">
<value>classpath*:**/grails-app/**/*.groovy</value>
</property>
</bean>
<bean id="characterEncodingFilter"
class="org.springframework.web.filter.CharacterEncodingFilter">
<property name="encoding">
<value>utf-8</value>
</property>
</bean>
<jaxws:server id="jaxwsService" serviceClass="at.pdts.cxf.HelloWorld" address="/hello">
<jaxws:serviceBean>
<bean class="at.pdts.cxf.HelloWorldImpl">
<property name="halloService"><ref bean="halloService"></ref></property>
</bean>
</jaxws:serviceBean>
</jaxws:server>
</beans>
HelloWorldImpl.groovy
package at.pdts.cxf
import javax.jws.WebService
#WebService(endpointInterface = "at.pdts.cxf.HelloWorld")
public class HelloWorldImpl implements HelloWorld {
def halloService // inject HelloService. Initialize this bean via applicationContext.xml
public String sayHi(String text) {
return "hello " + halloService.scream(text)
}
}
HelloService.groovy
class HalloService implements InitializingBean{
static transactional = false
String scream(String text) {
text.toUpperCase()
}
// methods gets not called, so service bean is not initialized at the ws creation time
void afterPropertiesSet() {
println "------> initializing bean HalloSerivce <--------
}
}
It seems that at the moment of the jaxwsService initialization the helloWorld service bean is not available.
This needs to point to something:
<ref bean="helloWorld">
Do you have something like this defined:
<bean id="helloWorld" class="at.pdts.cxf.HalloServiceImpl" />
That error means that Spring could now find a spring bean with the alias "helloWorld."
Perhaps posting your entire spring.xml and the java code to HelloWorldImpl would help.
EDIT: Your config confirms my theory.
<ref bean= says "inject something else here". But you have not defined that bean, hence the exception No Such Bean Definition. Furthermore, I was able to make your code work by creating my own implementation of HalloService (HalloServiceImpl) with a custom scream method that returned a blank string. Then I added it to the spring configuration: <bean id="helloWorld" class="at.pdts.cxf.HalloServiceImpl" />
EDIT #2: Another way to make it work is by eliminating HalloService:
<jaxws:server id="jaxwsService" serviceClass="at.pdts.cxf.HelloWorld" address="/hello">
<jaxws:serviceBean>
<bean class="at.pdts.cxf.HelloWorldImpl" />
</jaxws:serviceBean>
</jaxws:server>
</beans>
HelloWorldImpl.groovy
package at.pdts.cxf
import javax.jws.WebService
#WebService(endpointInterface = "at.pdts.cxf.HelloWorld")
public class HelloWorldImpl implements HelloWorld {
public String sayHi(String text) {
return "hello scream!" + text
}
}
Basically your choices are: Provide Spring an implmentation of HalloService, or don't reference it in your Spring.xml.
EDIT #3: There is a misunderstanding around the purpose of InitializingBean:
From the javadoc:
InitializingBean Interface to be implemented by beans that need to
react once all their properties have been set by a BeanFactory: for
example, to perform custom initialization, or merely to check that all
mandatory properties have been set.
Implementing InitializingBean just means that afterPropertiesSet() will be called. It does not mean the Spring will automatically add this bean to your Spring Config. You still must declare the bean in your spring configuration with this line:
<bean id="halloService" class="at.pdts.cxf.HalloService" />
I missed this the first time I read your question but you are defining your bean in applicationContext.xml. When I was making a test case for your question, I was putting my bean definition in grails-app/conf/spring/resources.xml. Try creating that file and putting the following into it:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<!--create the bean for the service, link to groovy service bean -->
<jaxws:server id="jaxwsService" serviceClass="at.pdts.cxf.HelloWorld" address="/hello">
<jaxws:serviceBean>
<bean class="at.pdts.cxf.HelloWorldImpl">
<property name="halloService" ref="halloService" />
</bean>
</jaxws:serviceBean>
</jaxws:server>
</beans>
As a side note, you can find more information about integrating Grails and CXF here.