com.mohendra.user
server
Application.class //Main class
package2
package3
domain
Campaigns.class
SmsDomainPackage.class
repository
CampaignRepository.class
The above is my folder structure, I am tryring to test CampaignRepository using spring dataJpaTest ,
I have written the following test
#ComponentScan(basePackages = "com.mohendra.user")
#EntityScan(basePackageClasses = SmsDomainPackage.class)
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#DataJpaTest
#RestClientTest
public class CampaignRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private CampaignRepository repository;
#Before
public void setUp() throws Exception {
}
#Test
public void findByCode() {
Campaigns campaigns = new Campaigns();
campaigns.setName("Name");
campaigns.setCode("HELP123");
campaigns.setStartDate(new Date());
campaigns.setEndDate(new Date());
this.entityManager.persist(campaigns);
Campaigns campaigns1 = repository.findByCode("HELP123");
System.out.println();
}
}
The test gives an exception of
java.lang.IllegalArgumentException: Unknown entity:
com.mohendra.user.package3.domain.Campaigns
I have also used #ComponentScan as you can see, and I've also used #EntityScan to try scan entities from packages, but both of them dont work.
I cannot change my folder structure to make it standard, as it is not my project. Is there a solution to it?
The Application class should be in the root package. That way you will not need any #CompontenScan or #EntityScan because Spring Boot scans everything below your root package
Therefor I recomment to put Application.class in the package com.mohendra.user
And you have to decide which test slice you want. You have three:
#SpringBootTest(classes = Application.class)
#DataJpaTest
#RestClientTest
But I assume that you only want #DataJpaTest
I want a repository (say, UserRepository) created with the help of Spring Data. I am new to spring-data (but not to spring) and I use this tutorial. My choice of technologies for dealing with the database is JPA 2.1 and Hibernate. The problem is that I am clueless as to how to write unit tests for such a repository.
Let's take create() method for instance. As I am working test-first, I am supposed to write a unit test for it - and that's where I bump into three problems:
First, how do I inject a mock of an EntityManager into the non-existing implementation of a UserRepository interface? Spring Data would generate an implementation based on this interface:
public interface UserRepository extends CrudRepository<User, Long> {}
However, I don't know how to force it to use an EntityManager mock and other mocks - if I had written the implementation myself, I would probably have a setter method for EntityManager, allowing me to use my mock for the unit test. (As for actual database connectivity, I have a JpaConfiguration class, annotated with #Configuration and #EnableJpaRepositories, which programmatically defines beans for DataSource, EntityManagerFactory, EntityManager etc. - but repositories should be test-friendly and allow for overriding these things).
Second, should I test for interactions? It is hard for me to figure out what methods of EntityManager and Query are supposed to be called (akin to that verify(entityManager).createNamedQuery(anyString()).getResultList();), since it isn't me who is writing the implementation.
Third, am I supposed to unit-test the Spring-Data-generated methods in the first place? As I know, the third-party library code is not supposed to be unit-tested - only the code the developers write themselves is supposed to be unit-tested. But if that's true, it still brings the first question back to the scene: say, I have a couple of custom methods for my repository, for which I will be writing implementation, how do I inject my mocks of EntityManager and Query into the final, generated repository?
Note: I will be test-driving my repositories using both the integration and the unit tests. For my integration tests I am using an HSQL in-memory database, and I am obviously not using a database for unit tests.
And probably the fourth question, is it correct to test the correct object graph creation and object graph retrieval in the integration tests (say, I have a complex object graph defined with Hibernate)?
Update: today I've continued experimenting with mock injection - I created a static inner class to allow for mock injection.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#Transactional
#TransactionConfiguration(defaultRollback = true)
public class UserRepositoryTest {
#Configuration
#EnableJpaRepositories(basePackages = "com.anything.repository")
static class TestConfiguration {
#Bean
public EntityManagerFactory entityManagerFactory() {
return mock(EntityManagerFactory.class);
}
#Bean
public EntityManager entityManager() {
EntityManager entityManagerMock = mock(EntityManager.class);
//when(entityManagerMock.getMetamodel()).thenReturn(mock(Metamodel.class));
when(entityManagerMock.getMetamodel()).thenReturn(mock(MetamodelImpl.class));
return entityManagerMock;
}
#Bean
public PlatformTransactionManager transactionManager() {
return mock(JpaTransactionManager.class);
}
}
#Autowired
private UserRepository userRepository;
#Autowired
private EntityManager entityManager;
#Test
public void shouldSaveUser() {
User user = new UserBuilder().build();
userRepository.save(user);
verify(entityManager.createNamedQuery(anyString()).executeUpdate());
}
}
However, running this test gives me the following stacktrace:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:101)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:319)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:212)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1493)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:684)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:121)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:250)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:64)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)
... 28 more
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:108)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:62)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1489)
... 44 more
tl;dr
To make it short - there's no way to unit test Spring Data JPA repositories reasonably for a simple reason: it's way to cumbersome to mock all the parts of the JPA API we invoke to bootstrap the repositories. Unit tests don't make too much sense here anyway, as you're usually not writing any implementation code yourself (see the below paragraph on custom implementations) so that integration testing is the most reasonable approach.
Details
We do quite a lot of upfront validation and setup to make sure you can only bootstrap an app that has no invalid derived queries etc.
We create and cache CriteriaQuery instances for derived queries to make sure the query methods do not contain any typos. This requires working with the Criteria API as well as the meta.model.
We verify manually defined queries by asking the EntityManager to create a Query instance for those (which effectively triggers query syntax validation).
We inspect the Metamodel for meta-data about the domain types handled to prepare is-new checks etc.
All stuff that you'd probably defer in a hand-written repository which might cause the application to break at runtime (due to invalid queries etc.).
If you think about it, there's no code you write for your repositories, so there's no need to write any unittests. There's simply no need to as you can rely on our test base to catch basic bugs (if you still happen to run into one, feel free to raise a ticket). However, there's definitely need for integration tests to test two aspects of your persistence layer as they are the aspects that related to your domain:
entity mappings
query semantics (syntax is verified on each bootstrap attempt anyway).
Integration tests
This is usually done by using an in-memory database and test cases that bootstrap a Spring ApplicationContext usually through the test context framework (as you already do), pre-populate the database (by inserting object instances through the EntityManager or repo, or via a plain SQL file) and then execute the query methods to verify the outcome of them.
Testing custom implementations
Custom implementation parts of the repository are written in a way that they don't have to know about Spring Data JPA. They are plain Spring beans that get an EntityManager injected. You might of course wanna try to mock the interactions with it but to be honest, unit-testing the JPA has not been a too pleasant experience for us as well as it works with quite a lot of indirections (EntityManager -> CriteriaBuilder, CriteriaQuery etc.) so that you end up with mocks returning mocks and so on.
With Spring Boot + Spring Data it has become quite easy:
#RunWith(SpringRunner.class)
#DataJpaTest
public class MyRepositoryTest {
#Autowired
MyRepository subject;
#Test
public void myTest() throws Exception {
subject.save(new MyEntity());
}
}
The solution by #heez brings up the full context, this only bring up what is needed for JPA+Transaction to work.
Note that the solution above will bring up a in memory test database given that one can be found on the classpath.
This may come a bit too late, but I have written something for this very purpose. My library will mock out the basic crud repository methods for you as well as interpret most of the functionalities of your query methods.
You will have to inject functionalities for your own native queries, but the rest are done for you.
Take a look:
https://github.com/mmnaseri/spring-data-mock
UPDATE
This is now in Maven central and in pretty good shape.
If you're using Spring Boot, you can simply use #SpringBootTest to load in your ApplicationContext (which is what your stacktrace is barking at you about). This allows you to autowire in your spring-data repositories. Be sure to add #RunWith(SpringRunner.class) so the spring-specific annotations are picked up:
#RunWith(SpringRunner.class)
#SpringBootTest
public class OrphanManagementTest {
#Autowired
private UserRepository userRepository;
#Test
public void saveTest() {
User user = new User("Tom");
userRepository.save(user);
Assert.assertNotNull(userRepository.findOne("Tom"));
}
}
You can read more about testing in spring boot in their docs.
In the last version of spring boot 2.1.1.RELEASE, it is simple as :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SampleApplication.class)
public class CustomerRepositoryIntegrationTest {
#Autowired
CustomerRepository repository;
#Test
public void myTest() throws Exception {
Customer customer = new Customer();
customer.setId(100l);
customer.setFirstName("John");
customer.setLastName("Wick");
repository.save(customer);
List<?> queryResult = repository.findByLastName("Wick");
assertFalse(queryResult.isEmpty());
assertNotNull(queryResult.get(0));
}
}
Complete code:
https://github.com/jrichardsz/spring-boot-templates/blob/master/003-hql-database-with-integration-test/src/test/java/test/CustomerRepositoryIntegrationTest.java
When you really want to write an i-test for a spring data repository you can do it like this:
#RunWith(SpringRunner.class)
#DataJpaTest
#EnableJpaRepositories(basePackageClasses = WebBookingRepository.class)
#EntityScan(basePackageClasses = WebBooking.class)
public class WebBookingRepositoryIntegrationTest {
#Autowired
private WebBookingRepository repository;
#Test
public void testSaveAndFindAll() {
WebBooking webBooking = new WebBooking();
webBooking.setUuid("some uuid");
webBooking.setItems(Arrays.asList(new WebBookingItem()));
repository.save(webBooking);
Iterable<WebBooking> findAll = repository.findAll();
assertThat(findAll).hasSize(1);
webBooking.setId(1L);
assertThat(findAll).containsOnly(webBooking);
}
}
To follow this example you have to use these dependencies:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
With JUnit5 and #DataJpaTest test will look like (kotlin code):
#DataJpaTest
#ExtendWith(value = [SpringExtension::class])
class ActivityJpaTest {
#Autowired
lateinit var entityManager: TestEntityManager
#Autowired
lateinit var myEntityRepository: MyEntityRepository
#Test
fun shouldSaveEntity() {
// when
val savedEntity = myEntityRepository.save(MyEntity(1, "test")
// then
Assertions.assertNotNull(entityManager.find(MyEntity::class.java, savedEntity.id))
}
}
You could use TestEntityManager from org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager package in order to validate entity state.
I solved this by using this way -
#RunWith(SpringRunner.class)
#EnableJpaRepositories(basePackages={"com.path.repositories"})
#EntityScan(basePackages={"com.model"})
#TestPropertySource("classpath:application.properties")
#ContextConfiguration(classes = {ApiTestConfig.class,SaveActionsServiceImpl.class})
public class SaveCriticalProcedureTest {
#Autowired
private SaveActionsService saveActionsService;
.......
.......
}
you can use #DataJpaTest annotation that focuses only on JPA components. By default, it scans for #Entity classes and configures Spring Data JPA repositories annotated with #Repository annotation.
By default, tests annotated with #DataJpaTest are transactional and roll back at the end of each test.
//in Junit 5 #RunWith(SpringRunner.class) annotation is not required
#DataJpaTest
public class EmployeeRepoTest {
#Autowired
EmployeeRepo repository;
#Test
public void testRepository()
{
EmployeeEntity employee = new EmployeeEntity();
employee.setFirstName("Anand");
employee.setProject("Max Account");
repository.save(employee);
Assert.assertNotNull(employee.getId());
}
}
Junit 4 Syntax will be along with SpringRunner class.
//Junit 4
#RunWith(SpringRunner.class)
#DataJpaTest
public class DataRepositoryTest{
//
}
springboot 2.4.5
import javax.persistence.EntityManager;
import javax.persistence.ParameterMode;
import javax.persistence.PersistenceContext;
import javax.persistence.StoredProcedureQuery;
#Repository
public class MyRepositoryImpl implements MyRepository {
#Autowired
#PersistenceContext(unitName = "MY_JPA_UNIT")
private EntityManager entityManager;
#Transactional("MY_TRANSACTION_MANAGER")
#Override
public MyEntity getSomething(Long id) {
StoredProcedureQuery query = entityManager.createStoredProcedureQuery(
"MyStoredProcedure", MyEntity.class);
query.registerStoredProcedureParameter("id", Long.class, ParameterMode.IN);
query.setParameter("id", id);
query.execute();
#SuppressWarnings("unchecked")
MyEntity myEntity = (MyEntity) query.getResultList().stream().findFirst().orElse(null);
return myEntity;
}
}
import org.junit.jupiter.api.*;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import javax.persistence.EntityManager;
import javax.persistence.StoredProcedureQuery;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
#RunWith(MockitoJUnitRunner.Silent.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MyRepositoryTest {
#InjectMocks
MyRepositoryImpl myRepository;
#Mock
private EntityManager entityManager;
#Mock
private StoredProcedureQuery storedProcedureQuery;
#BeforeAll
public void init() {
MockitoAnnotations.openMocks(this);
Mockito.when(entityManager.createStoredProcedureQuery(Mockito.any(), Mockito.any(Class.class)))
.thenReturn(storedProcedureQuery);
}
#AfterAll
public void tearDown() {
// something
}
#Test
void testMethod() throws Exception {
Mockito.when(storedProcedureQuery.getResultList()).thenReturn(List.of(myEntityMock));
MyEntity resultMyEntityList = myRepository.getSomething(1l);
assertThat(resultMyEntityList,
allOf(hasProperty("id", org.hamcrest.Matchers.is("1"))
. . .
);
}
}
In 2021 with a new initalized springboot 2.5.1 project, I'm doing it like:
...
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
#ExtendWith(MockitoExtension.class)
#DataJpaTest
public class SomeTest {
#Autowired
MyRepository repo;
#Test
public void myTest() throws Exception {
repo.save(new MyRepoEntity());
/*...
/ Actual Test. For Example: Will my queries work? ... etc.
/ ...
*/
}
}
I have two questions:
1) How do you set the servlet Context for a Unit Test that extends JerseyTest. From the research I've done, it seems that I have to create a TestContainer for a TestFactory and pass in an AppDescriptor, but that seems more complicated then it should be. Is there any other suggestions out there?
In general, looking for a way to set the Servlet Context in a unit test of my Jersey Resource Class which is done normally by the web.xml.
Example:
#Path(value = "/service")
public class Foo{
#Context ServletContext ctx;
#GET
#Path(value="/list")
public String list() {
Controller ctrl = new Controller();
ctx.setAttribute("controller", ctrl);
return ctrl.getList();
}
}
public class FooUnitTest extends JerseyTest
{
#Test
public void testService()
{
//set/how to configure the context?
}
}
The goal is to mock out the Controller so I can pass it into the context.
2) What's the difference between using the jersey test from the
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly</artifactId>
<version>1.5-SNAPSHOT</version>
<scope>test</scope>
</dependency>
vs.
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-bundle</artifactId>
<version>2.4.1</version>
</dependency>
This is Jersey framework 2.5
You can use org.springframework.mock.web.MockServletContext class to mock servlet context. It's a part of Spring framework.
MockServletContext
I'm trying to implement simple web service client for PayPal Express Checkout API using JAX WS. PayPal Express Checkout API provides WSDL file, from which I was able to generate Java classes using CXF's wsdl2java utility.
From authentication reasons, it demands adding SOAP Header to each request. This header is quite simple and should look like here:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ECSOAPAPIBasics#id09C3I0CF0O6
Generated from WSDL classes include ebay.apis.eblbasecomponents.CustomSecurityHeaderType class which represents header which I need to add to each request.
So the question is: how can I add manually created instance of CustomSecurityHeaderType class to SOAP request's header taking into account following conditions:
I'm not very eager to use classes from com.sun.* package as mentioned in answer here: JAX-WS - Adding SOAP Headers (mainly because of possible portability issues between different JDK's)
I don't want to manually marshal that object into nested javax.xml.soap.SOAPElement instances as mentioned in answer here:
How do I add a SOAP Header using Java JAX-WS
So, it looks like I've found possible answer while combining JAX-WS & JAXB related answers from SO (I would really appreciate if somebody experienced in these technologies can check whether following is correct):
The obvious thing for me is to add SOAP message handler and alter header of SOAPMessage instance in it:
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.soap.SOAPHeader;
import ebay.api.paypalapi.ObjectFactory; // class generated by wsdl2java
// following class is generated by wsdl2java utility Service class
final PayPalAPIInterfaceService payPalService = new PayPalAPIInterfaceService();
final PayPalAPIAAInterface expressCheckoutPort = payPalService.getPayPalAPIAA();
final Binding binding = ((BindingProvider) expressCheckoutPort).getBinding();
List<Handler> handlersList = new ArrayList<Handler>();
// now, adding instance of Handler to handlersList which should do our job:
// creating header instance
final CustomSecurityHeaderType headerObj = new CustomSecurityHeaderType();
final UserIdPasswordType credentials = new UserIdPasswordType();
credentials.setUsername("username");
credentials.setPassword("password");
credentials.setSignature("signature");
headerObj.setCredentials(credentials);
// bookmark #1 - please read explanation after code
final ObjectFactory objectFactory = new ObjectFactory();
// creating JAXBElement from headerObj
final JAXBElement<CustomSecurityHeaderType> requesterCredentials = objectFactory.createRequesterCredentials(headerObj);
handlersList.add(new SOAPHandler<SOAPMessageContext>() {
#Override
public boolean handleMessage(final SOAPMessageContext context) {
try {
// checking whether handled message is outbound one as per Martin Strauss answer
final Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound");
if (outbound != null && outbound) {
// obtaining marshaller which should marshal instance to xml
final Marshaller marshaller = JAXBContext.newInstance(CustomSecurityHeaderType.class).createMarshaller();
// adding header because otherwise it's null
final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader();
// marshalling instance (appending) to SOAP header's xml node
marshaller.marshal(requesterCredentials, soapHeader);
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
return true;
}
// ... default implementations of other methods go here
});
// as per Jean-Bernard Pellerin's comment setting handlerChain list here, after all handlers were added to list
binding.setHandlerChain(handlersList);
Explanation of bookmark #1:
one should marshal not the header object itself, but JAXBElement representing that object, because otherwise one will get an exception. One should use one of ObjectFactory classes which are generated from WSDL for creating needed JAXBElement instances from original objects.
(Thanks #skaffman for answer: No #XmlRootElement generated by JAXB )
One should also refer to Martin Straus answer which extends this one
This solution works great, but there's a catch. It generates this error when the inbound message is processed:
dic 19, 2012 7:00:55 PM com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl addHeader
SEVERE: SAAJ0120: no se puede agregar una cabecera si ya hay una
Exception in thread "main" javax.xml.ws.WebServiceException: java.lang.RuntimeException: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
at com.sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.java:167)
at com.sun.xml.ws.handler.HandlerTube.processResponse(HandlerTube.java:174)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1074)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:979)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:950)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:825)
at com.sun.xml.ws.client.Stub.process(Stub.java:443)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:174)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:102)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:154)
at $Proxy38.wsRdyCrearTicketDA(Unknown Source)
at ar.com.fit.fides.remedy.api.ws.ServicioCreacionTickets.crearTicket(ServicioCreacionTickets.java:55)
at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.crearTicket(ConectorRemedyWS.java:43)
at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.main(ConectorRemedyWS.java:90)
Caused by: java.lang.RuntimeException: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:50)
at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:23)
at com.sun.xml.ws.handler.HandlerProcessor.callHandleMessageReverse(HandlerProcessor.java:341)
at com.sun.xml.ws.handler.HandlerProcessor.callHandlersResponse(HandlerProcessor.java:214)
at com.sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.java:161)
... 14 more
Caused by: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
at com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.java:128)
at com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.java:108)
at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:45)
So, the solution is to check whether the message being handled if the outbound message, like this:
public boolean handleMessage(SOAPMessageContext context) {
try {
Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound");
if (outbound != null && outbound) {
// obtaining marshaller which should marshal instance to xml
final Marshaller marshaller = JAXBContext.newInstance(AuthenticationInfo.class).createMarshaller();
// adding header because otherwise it's null
final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader();
// marshalling instance (appending) to SOAP header's xml node
marshaller.marshal(info, soapHeader);
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
return true;
}
I created a web service exposing method with params user and password as header like this:
#WebService(serviceName="authentication")
public class WSAuthentication {
String name = null;
String password = null;
public WSAuthentication() {
super();
}
public WSAuthentication(String name, String password) {
this.name = name;
this.password = password;
}
private static String getData(WSAuthentication sec) {
System.out.println("********************* AUTHENTICATION ********************" + "\n" +
"**********USER: " + sec.name + "\n" +
"******PASSWORD: " + sec.password + "\n" +
"******************************** AUTHENTICATION ****************************");
return sec.name + " -- " + sec.password;
}
#WebMethod(operationName="security", action="authenticate")
#WebResult(name="answer")
public String security(#WebParam(header=true, mode=Mode.IN, name="user") String user, #WebParam(header=true, mode=Mode.IN, name="password") String password) {
WSAuthentication secure = new WSAuthentication(user, password);
return getData(secure);
}
}
Try compiling it and testing generated from WSDL class. I hope this helps.
I found this answer:
JAX-WS - Adding SOAP Headers
Basically you add -XadditionalHeaders to the compiler options and objects in the headers also appear in your generated code as parameters of the method.
If you are using maven, and the jaxws-maven-plugin all you have to do is add the xadditionalHeaders flag to true and the client will be generated with the methods that have the headers as input.
https://jax-ws-commons.java.net/jaxws-maven-plugin/wsimport-mojo.html#xadditionalHeaders
I want to write test that verifies mappings in castle windsor.
I am using ASP MVC2 where i am using castle windsor to map my repositories.
I have read this article:
http://weblogs.asp.net/bsimser/archive/2008/06/04/the-first-spec-you-should-write-when-using-castle.aspx
and based on this i have created my MS Test
[TestMethod()]
public void GetContainerTest()
{
MooseMvc.Infrastructure.DependencyInjectionInitialiser target = new MooseMvc.Infrastructure.DependencyInjectionInitialiser(); // TODO: Initialize to an appropriate value
IWindsorContainer container = target.GetContainer();
foreach (IHandler assignableHandler in container.Kernel.GetAssignableHandlers(typeof(object)))
{
container.Resolve(assignableHandler.ComponentModel.Service);
}
}
The data for target.getcontainer() implements
this._windsorContainer.Register(Component.For<TInterfaceType>()
.ImplementedBy(typeof(TConcreteType)).LifeStyle.PerWebRequest);
I get message as follows:
Looks like you forgot to register the http module
Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule Add '<add
name="PerRequestLifestyle"
type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule,
Castle.Windsor" />' to the <httpModules> section on your web.config.
If you're running IIS7 in Integrated Mode you will need to add it to
<modules> section under <system.webServer>
I have had the same problem and I have found a solution: You can define an event in the contructor of the unit test to override the LifestyleType.
void Kernel_ComponentModelCreated(Castle.Core.ComponentModel model)
{
if (model.LifestyleType == LifestyleType.Undefined)
model.LifestyleType = LifestyleType.Transient;
if (model.LifestyleType == LifestyleType.PerWebRequest)
model.LifestyleType = LifestyleType.Transient;
}
public UnitTest1()
{
containerWithControllers = new WindsorContainer();
containerWithControllers.Kernel.ComponentModelCreated += new ComponentModelDelegate(Kernel_ComponentModelCreated);
}
i have found beautifull guide
http://docs.castleproject.org/Windsor.Windsor-tutorial-part-three-a-testing-your-first-installer.ashx
there is not much else to add..