Consume webservice service in SPRING-WS using wsdl - spring

I have WSDL with me .eg: /sample/hello?wsdl . I want to invoke the service the webservice by configuring in Spring-ws. I passed this wsdl as parameter to tags in springconfig.xml.
Can anyone please tell me how to consume this webservice in Spring-ws.

1. Set up project dependencies
add the following dependencies to the pom file:
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.5</version>
</dependency>
2. Set up web service application context
<?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-3.1.xsd">
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.yourcomany.model" />
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory" />
<property name="marshaller" ref="marshaller"></property>
<property name="unmarshaller" ref="marshaller"></property>
<property name="messageSender">
<bean
class="org.springframework.ws.transport.http.HttpComponentsMessageSender" />
</property>
<property name="defaultUri"
value="http://<hostname>:<portnumber>/sample/hello" />
</bean>
</beans>
3. Set up model classes which would map to your SOAP request/response objects
For example, if your SOAP request XML looked like
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xxx="http://yourcomapny.com">
<soapenv:Header/>
<soapenv:Body>
<xxx:InputParameters>
<xxx:paramONE>1</xxx:paramONE>
</xxx:InputParameters>
</soapenv:Body>
</soapenv:Envelope>
and your SOAP response XML looked like:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
...
</env:Header>
<env:Body>
<xxx:OutputParameters xmlns:xxx="http://yourcompany.com">
<xxx:paramONE>0</xxx:paramONE>
</xxx:OutputParameters>
</env:Body>
</env:Envelope>
the corresponding classes (under the package you specified in the marshaller bean: com.yourcompany.model) would be respectively:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "paramONE" })
#XmlRootElement(name = "InputParameters", namespace = "http://yourcompany.com")
public class InputParameters {
#XmlElement(required = true, namespace = "http://yourcompany.com")
private String paramONE;
public String getParamONE() {
return paramONE;
}
public void setParamONE(String paramONE) {
this.paramONE = paramONE;
}
}
and
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "paramONE" })
#XmlRootElement(name = "OutputParameters", namespace = "http://yourcompany.com")
public class OutputParameters {
#XmlElement(required = true, namespace = "http://yourcompany.com")
private BigDecimal paramONE;
public BigDecimal getParamONE() {
return this.paramONE;
}
public void setParamONE(BigDecimal paramONE) {
this.paramONE= paramONE;
}
}
4. Add an Object Factory (under package com.yourcompany.model) to create request/response objects
#XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
public InputParameters createYourRequest() {
return new InputParameters();
}
public OutputParameters createYourResponse() {
return new OutputParameters();
}
}
5. Create a client to consume the service
Interface:
public interface YourService {
BigDecimal getValue(String paramOne);
}
Implementation
#Component("yourServiceClient")
public class YourServiceClient implements YourService {
private static final ObjectFactory WS_CLIENT_FACTORY = new ObjectFactory();
private WebServiceTemplate webServiceTemplate;
#Autowired
public YourServiceClient(WebServiceTemplate webServiceTemplate) {
this.webServiceTemplate = webServiceTemplate;
}
#Override
public BigDecimal getValue(String paramOne) {
InputParameters request = WS_CLIENT_FACTORY
.createYourRequest();
request.setParamONE(paramOne);
OutputParameters response = (OutputParameters) webServiceTemplate
.marshalSendAndReceive(request);
return response.getParamONE();
}
}

#Taoufik Mohdit answer is complete!!
To build the input and output objects you can use Webservice-Client: Common approach with Spring WS, JAXB and just one WSDL file? to some how build these objects automatically

Given that this question is still active I thought I would post an update that reflects a number of changes that the recent version of the Spring Web Services framework and Spring in general introduce:
The introduction of Spring Boot allows to leverage 'starter' POMs to simplify your Maven configuration. There is a specific spring-boot-starter-web-services starter for Spring-WS
Instead of specifying Spring configuration files using XML, Spring JavaConfig was introduced which provides a type-safe, pure-Java option for configuring Spring.
Generation of request/response objects based on a given WSDL file can be automated using Maven plugins. The plugin used by the Spring-WS examples is the maven-jaxb2-plugin.
The WebServiceTemplate is still the core class for client-side Web service access. For more information you can check this detailed example on how to consume a web service using Spring-WS starting from a WSDL file that I wrote.

Related

Change Implementation for Spring Namespaces (exchange a bean)

I have a question about Spring Integration (or basically Spring in common):
I use the a WebService Inbound Gateway in my Spring XML configuration:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
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
http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd" >
<int-ws:inbound-gateway
id="ws-in-gw-user"
request-channel="in-user"
reply-channel="out-user"
mapped-request-headers="*"
/>
...
</beans>
When I use <int-ws:inbound-gateway> Tag, a SimpleWebServiceInboundGateway is created. Now I want to exchange this implemantation with a self written extension of this class. Any ideas how to do it?
you could use a BeanPostProcessor bean.
import org.springframework.beans.factory.config.BeanPostProcessor;
public class SimpleWebServiceInboundGatewayBeanPostProcessor implements BeanPostProcessor{
public Object postProcessBeforeInitialization(Object bean, String beanName){
if(bean instanceof SimpleWebServiceInboundGateway) {
return new MyCustomWebSerivceInboundGateway();
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
In your config:
<bean class="....SimpleWebServiceInboundGatewayBeanPostProcessor" />
A BeanPostProcessor allows you to exhange beans during initialization.

Spring MVC validation ignored

I have Spring MVC app on Tomcat. I'm trying to add validation in it.
For some reason my validation is ignored and not checked.
Here is my POJO
import org.hibernate.validator.constraints.Range;
public class Goal {
#Range(min = 1, max = 15)
private int minutes;
public int getMinutes() {
return minutes;
}
public void setMinutes(int minutes) {
this.minutes = minutes;
}
}
And controller
import org.springframework.validation.BindingResult;
import javax.validation.Valid;
#Controller
#SessionAttributes("goal")
public class GoalController {
#RequestMapping(value = "addGoal", method = RequestMethod.POST)
public String updGoal(#ModelAttribute("goal") #Valid Goal goal, BindingResult result) {
System.out.println(goal.getMinutes());
System.out.println(result.hasErrors());
if(result.hasErrors()) {
return "addGoal";
}
return "redirect:addMinutes.html";
}
}
But even if I try to put any negative values as "minutes" there no errors produced.
Added configuration
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.xlab.ice.mvc.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
<mvc:resources mapping="/pdfs/**" location="pdf"/>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basename="messages"/>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" p:defaultLocale="en"/>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>
</mvc:interceptors>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0"/>
</beans>
Can you please also confirm that you have a JSR 303 implementation in your classpath - say hibernate-validator jar files.
You need <mvc:annotation-driven /> to enable jsr-303 validation, This is needed for the #Valid annotation to actually do anything.
try adding <mvc:annotation-driven/> in servlet context XML if not done.
schemaLocation the mvc entry should contain these two:
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
Solution is simple.
Validation libraries must be placed into classpath.
DISCLAIMER : In the absence of complete relevant controller code and the suspicion that Validator library might be missing in classpath, here is a small test to just check the configuration. You can use this very quickly to find if all configurations are in place with just removing the session attribute part.
Let's give it a try. To simplify testing with a REST Client and also incomplete code I have not used SessionAttributes. Also there is Hibernate validator library on my runtime classpath. Also I have used ResponseBody annotation to let the Spring automatically convert the String to response text without making use of alternate view technology (like jsp).
import org.hibernate.validator.constraints.Range;
public class Goal {
#Range(min = 1, max = 15)
private int minutes;
public int getMinutes() {
return minutes;
}
public void setMinutes(int minutes) {
this.minutes = minutes;
}
}
Then my controller
#Controller
public class GoalController {
#RequestMapping(value = "/addGoal", method = RequestMethod.POST)
public #ResponseBody String updGoal(#ModelAttribute("goal") #Valid Goal goal, BindingResult result) {
System.out.println(goal.getMinutes());
System.out.println(result.hasErrors());
if(result.hasErrors()) {
return "Errors !";
}
return "No Errors !";
}
}
Hitting the application with a rest client on http://localhost:8080/testApp/addGoal with POST parameters as minutes=1 gives me response as No Errors ! while with minutes=-1 gives me Errors !

PropertyPlaceholderConfigurer not loading programmatically

I have a custom ApplicationContext class where I'm trying to programmatically load a PropertyPlaceholderConfigurer, then use the placeholders in my XML config file.
I've tried three different approaches so far, and each time I get an error like this:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named '${domain}.testId' is defined
What I am doing wrong?
Context class
public class MyApplicationContext extends GenericApplicationContext {
public MyApplicationContext (String... locations) throws IOException {
// method one
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this);
scanner.scan("com.my.package");
// method two
new MyPropertyPlaceholderConfigurer().postProcessBeanFactory(getBeanFactory());
// method three
getBeanFactory().registerSingleton("propertyPlaceholderConfigurer", new MyPropertyPlaceholderConfigurer());
// load XML config files
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(this);
for (String location : locations) {
xmlReader.loadBeanDefinitions(location);
}
}
}
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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="test.testId" class="java.lang.String">
<constructor-arg value="this is the test value" />
</bean>
<bean id="prod.testId" class="java.lang.String">
<constructor-arg value="this is the prod value" />
</bean>
<alias name="${domain}.testId" alias="testId" />
</beans>
Usage
MyApplicationContext context = new MyApplicationContext(
new String[] { "test.xml" });
Assert.assertEquals("this is the test value", context.getBean("testId"));
I appreciate the answers on how to define the bean, but it turns out the problem was much simpler. I was loading the bean just fine, but I wasn't properly initializing my context after loading all the beans. A simple call to refresh() did the trick.
MyApplicationContext context = new MyApplicationContext(
new String[] { "test.xml" });
context.refresh(); // this runs BeanFactoryPostProcessors like
// MyPropertyPlaceholderConfigurer, among other things
Assert.assertEquals("this is the test value", context.getBean("testId"));
If you just want to use your own custom MyPropertyPlaceholderConfigurer (assuming it extends PropertyPlaceholderConfigurer, then all you need to do is to put it as a bean in your application context, it'll do all the rest for you:
<bean class="my.pkg.MyPropertyPlaceholderConfigurer" />
This is standard way how make your properties accessible in configuration file.
<util:list id="locations">
<value>classpath:appconfig.properties</value>
<value>classpath:jdbc.properties</value>
</util:list>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:ignoreResourceNotFound="true"
p:locations-ref="locations" />
<!-- bean that uses properties -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${jdbc.driver}"
p:jdbcUrl="${jdbc.url}"
p:user="${jdbc.user}"
p:password="${jdbc.pw}"
p:initialPoolSize="5"
p:minPoolSize="5"
p:maxPoolSize="50"
p:idleConnectionTestPeriod="5" />
Another quite useful way how to "inject" properties in your configuration file on ANY place is using maven: you use same syntax ${property-name} but values are supplied earlier during maven build process - this is relevant part of maven configuration:
<properties>
<jdbc.url>jdbc:mysql://localhost:3306/dummyuserdb</jdbc.url>
<jdbc.user>root</jdbc.user>
<jdbc.pw>admin</jdbc.pw>
</properties>
and you have to include your spring's configuration directory in maven filtered resources:
<build>
<resources>
<resource>
<directory>${basedir}/src/main/webapp/WEB-INF/spring-config</directory>
<filtering>true</filtering>
</resource>
</resources>
Or if you prefer java configuration:
#Configuration
#PropertySource(value = "config/jdbc.properties")
public class BaseWebConfig {
#Autowired Environment env;
#Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
String propertyValue = env.getProperty("my-property-name");
// do something with myBean and propertyValue
return myBean;
}
}
}

Spring and auto-wiring: NullPointerException

I'm trying to get a grip on auto-wiring in Spring, but I can't seem to properly instantiate the bean (a DocumentBuilder). I have created a custom JSP tag as such:
public class MyTag extends SimpleTagSupport {
#Autowired
private DocumentBuilder documentBuilder;
public void setBuilder(DocumentBuilder builder) {
this.documentBuilder = builder;
}
#Override
public void doTag() throws IOException {
// documentBuilder is null in here!
}
}
This is the servlet configuration:
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-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/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Scan for HTTP/REST controllers -->
<context:component-scan base-package="the.right.package" />
<context:annotation-config/>
<bean id="documentBuilderFactory"
class="javax.xml.parsers.DocumentBuilderFactory"
factory-method="newInstance">
<property name="validating" value="false" />
<property name="ignoringElementContentWhitespace" value="true" />
</bean>
<bean id="documentBuilder" class="javax.xml.parsers.DocumentBuilder"
factory-bean="documentBuilderFactory"
factory-method="newDocumentBuilder">
</bean>
</beans>
Any ideas?
You can only inject in spring beans! But Jsp-Tags are no Spring Beans, so the Autowird annotation will be completely ignored, and therefore the field is null.
There are two solution:
use the #Configurable Support. -- But that requires real AspectJ. (I have never tried it for Tags, but I guess it will work for tags like for every other normal class). #see Spring Reference: Chapter 7.8.1 Using AspectJ to dependency inject domain objects with Spring
Extend your tag from the abstract Spring class RequestContextAwareTag. This provides access to the WebApplicationContext via getRequestContext().getWebApplicationContext(). Then you can use the WebApplicationContext to obtain the required beans programmatic.
Try to modify the code like this
public class MyTag extends SimpleTagSupport {
private DocumentBuilder documentBuilder;
#Autowired
public void setBuilder(DocumentBuilder builder) {
this.documentBuilder = builder;
}
#Override
public void doTag() throws IOException {
// documentBuilder is null in here!
}
}
You can use #Autowired if you mark your tag class as Spring bean. But it's stupid, because simple tags not caching by container. Each request creates own tag instance, but wiring happend only conteiner starts.

Why destroy-method is giving error in Spring MVC?

I have destroy method in my bean but it is not showing in the out put. Could you please help me here.
package com.vaannila;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloWorldApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Ticket helloWorld = (Ticket) context.getBean("ticket");
helloWorld.setTicketNo("ABC009");
helloWorld.display();
context.close();
}
}
below is my xml file
<?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="helloWorld" class="com.vaannila.HelloWorld">
<property name="message" value="Hello World!"></property>
</bean>
<bean id="ticket" class="com.vaannila.Ticket"
scope="prototype" init-method="init" destroy-method="destroy"/>
</beans>
and Ticket class is below
package com.vaannila;
public class Ticket {
private String ticketNo="";
public String getTicketNo() {
return ticketNo;
}
public void setTicketNo(String ticketNo) {
this.ticketNo = ticketNo;
}
public void display()
{
System.out.println("Your Ticket No. is"+ ticketNo);
}
public void init()
{
System.out.println("Bean is ready You can use it now");
}
public void destroy()
{
System.out.println("Bean is going to destroy");
}
}
The out put is giving for init method but not for destroy method..
If i changed the init-method and destroy-method as default as below it is giving error in destroying the bean called "helloWorld"
<?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"
default-init-method="init" default-destroy-method="destroy">
<bean id="helloWorld" class="com.vaannila.HelloWorld">
<property name="message" value="Hello World!"></property>
</bean>
<bean id="ticket" class="com.vaannila.Ticket"
scope="prototype"/>
</beans>
When a bean is defined as prototype, the bean container creates new instances of this been whenever it is asked for that bean. That's the idea behind prototype-scoped beans.
After they are created, the container gives up responsibility for the bean. It cannot know if you are still holding a reference to it, or when is the moment you drop the last reference. This is true even after the container is closed. (The container is not the garbage collector.) So it cannot possibly know when is the right moment to call the destroy method.
If you need deinitialization for your ticket, you will have to call such a method from your code directly I think (assuming that it makes no sense to have singleton tickets).

Resources