We are struggling to configure our web app to be able to connect with web services via Spring WS. We have tried to use the example from the documentation of client-side Spring-WS, but we end up with a WebServiceTransportException. The XML config looks like this:
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
<property name="credentials">
<bean class="org.apache.commons.httpclient.UsernamePasswordCredentials">
<constructor-arg value="john"/>
<constructor-arg value="secret"/>
</bean>
</property>
</bean>
</property>
</bean>
We have been able to configure the application programmatically, but this configuration was not possible to "transfer" to a Spring XML config because some setters did not use the format Spring expects. (HttpState.setCredentials(...) takes two parameters). The config was lifted from some other Spring-WS client code in the company.
This is the configuration that works:
public List<String> getAll() {
List<String> carTypes = new ArrayList<String>();
try {
Source source = new ResourceSource(request);
JDOMResult result = new JDOMResult();
SaajSoapMessageFactory soapMessageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
WebServiceTemplate template = new WebServiceTemplate(soapMessageFactory);
HttpClientParams clientParams = new HttpClientParams();
clientParams.setSoTimeout(60000);
clientParams.setConnectionManagerTimeout(60000);
clientParams.setAuthenticationPreemptive(true);
HttpClient client = new HttpClient(clientParams);
client.getState().setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("username", "password"));
CommonsHttpMessageSender messageSender = new CommonsHttpMessageSender(client);
template.setMessageSender(messageSender);
template.sendSourceAndReceiveToResult(SERVICE_URI,
source, result);
// Handle the XML
} catch (IOException e) {
throw new RuntimeException(e);
} catch (SOAPException e) {
throw new RuntimeException(e);
}
return carTypes;
}
Does anyone know how to solve my problem? Every tutorial I have see out there lists the first configuration. It seems that when I set the credentials on the messageSender object, they are just ignored...
Override HttpClient with a constructor that takes the parameters and wire through Spring using constructor-args
public MyHttpClient(HttpClientParams params, UsernamePasswordCredentials usernamePasswordCredentials) {
super(params);
getState().setCredentials(AuthScope.ANY, usernamePasswordCredentials);
}
How do you distinguish these:
<constructor-arg value="john"/>
<constructor-arg value="secret"/>
try and replace it with this:
<property name="userName" value="john" />
<property name="password" value="secret" />
Hope it helps.
If you are using a defaultHttpClient like you are in your example, Use the afterPropertiesSet method on your HTTPMessageSender and that should fix your problem by applying the credentials correctly
At first we were setting credentials in our project like this:
<bean id="authenticationEnabledCommonsHttpMessageSender" parent="commonsHttpMessageSender"
p:credentials-ref="clientCredentials" lazy-init="true" />
<bean id="clientCredentials"
class="org.apache.commons.httpclient.UsernamePasswordCredentials"
c:userName="${clientCredentials.userName}"
c:password="${clientCredentials.password}"
lazy-init="true" />
This is our cridentials enabled option. A problem occured while we are setting credentials like that.
If the server we send message (has Axis impl) has not got username password credentials we get "Unauthorized" exception. Because ,when we trace vie TCPMon, we realized "username:password:" string was sent, as you can see username and password have no value.
After that we set the credentials like that:
public Message sendRequest(OutgoingRequest message, MessageHeaders headers,
EndpointInfoProvider endpointInfoProvider,
WebServiceMessageCallback requestCallback){
Assert.notNull(endpointInfoProvider, "Destination provider is required!");
final Credentials credentials = endpointInfoProvider.getCredentials();
URI destinationUri = endpointInfoProvider.getDestination();
for (WebServiceMessageSender messageSender : webServiceTemplate.getMessageSenders()) {
if (messageSender instanceof CommonsHttpMessageSender) {
HttpClient httpClient = ((CommonsHttpMessageSender) messageSender).getHttpClient();
httpClient.getState().setCredentials(
new AuthScope(destinationUri.getHost(),
destinationUri.getPort(), AuthScope.ANY_REALM,
AuthScope.ANY_SCHEME), credentials
);
httpClient.getParams().setAuthenticationPreemptive(true);
((CommonsHttpMessageSender) messageSender)
.setConnectionTimeout(endpointInfoProvider
.getTimeOutDuration());
}
}
And the getCredentials methos is:
#Override
public Credentials getCredentials(){
if (credentials != null) {
return credentials;
}
String username = parameterService.usernameFor(getServiceName());
String password = parameterService.passwordFor(getServiceName());
if (username == null && password == null) {
return null;
}
credentials = new UsernamePasswordCredentials(username, password);
return credentials;
}
Related
I am using aws sqs in my current project. I need to know how to set access key and secret key (to be read from a properties file) using SQSConnectionFactory.
Please find my code below:
public void createConnectionFactory() throws JMSException{
try {
SQSConnectionFactory connectionFactory = SQSConnectionFactory.builder()
.withRegion(Region.getRegion(Regions.US_EAST_1))
.withAWSCredentialsProvider(new EnvironmentVariableCredentialsProvider())
.build();
// Create the connection.
SQSConnection connection = connectionFactory.createConnection();
}
catch (JMSException ex)
{
throw ex;
}
}
I tried searching methods through which I can set, but couldn't find any. How to proceed from here? Should I write another method for Basic Authentication and invoke it here or is there any better way to do this?
It's best to use the default credentials provider chain which allows to supply credentials in multiple ways
In summary it allows for a very flexible configuration for both your development teams and when deploy to the cloud with multiple environments and multi-region setup.
You can include the bean like this:
<bean id="awsCredentialsProviderBean" class="com.amazonaws.auth.DefaultAWSCredentialsProviderChain">
</bean>
<bean id="awsConnectionFactoryBuilder" class="com.amazon.sqs.javamessaging.SQSConnectionFactory$Builder">
<property name="regionName" value="${aws.region}"/>
<property name="numberOfMessagesToPrefetch" value="5"/>
<property name="awsCredentialsProvider" ref="awsCredentialsProviderBean"/>
</bean>
or use java
#Bean
public AWSCredentialsProviderChain awsCredentials() {
return new DefaultAWSCredentialsProviderChain();
}
Thanks , I found the mistake !
Here are the changes that I made and now its working fine
private BasicAWSCredentials credentials;
public void createConnectionFactory() throws JMSException,FileNotFoundException{
try {
Properties properties = new Properties();
properties.load(new FileInputStream("/src/main/resources/AwsCredentials.properties"));
this.credentials = new BasicAWSCredentials(properties.getProperty("accessKey"),
properties.getProperty("secretKey"));
SQSConnectionFactory connectionFactory =
SQSConnectionFactory.builder()
.withRegion(Region.getRegion(Regions.US_EAST_1))
.withAWSCredentialsProvider(new EnvironmentVariableCredentialsProvider())
.build();
// Create the connection.
SQSConnection connection = connectionFactory.createConnection(credentials.getAWSAccessKeyId(),credentials.getAWSSecretKey());
}
catch (JMSException ex)
{
throw ex;
}
catch (FileNotFoundException ex)
{
throw ex;
} catch (IOException e) {
e.printStackTrace();
}
My Spring RESTful web service is returning a JSON form of-
[{"key1":"value1","key2":"value2","key3":"value3"},{"key4":"value4","key5":"value5","key6":"value6"}]
Now when my spring MVC app, try to access it, to show in a JSP then Exception occurs saying-no suitable HttpMessageConverter found Please help me where I going wrong.Here is my code-
Inside #Controller class of my spring MVC app calling the RESTful service
//**com.songs.controllers.FrontSongController.java**
</*
author Rohit Tiwari
*/>
#RequestMapping(value="/alls",method=RequestMethod.POST)
public String getAllSongs(ModelMap md)
{
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
String url="http://localhost:7001/SongAppWS/songappWS/allsongsWS";
RestTemplate rt=new RestTemplate();
//SongResource.class is for representation on incoming JSON see below for its code
//This is the line no 48 that you will see in below browser logs
ResponseEntity<SongResource> listofallsongs=rt.exchange(url,HttpMethod.GET,entity, SongResource.class);
md.addAttribute("listname", "Songs available in the repository:");
System.out.println("Response Entity object= "+listofallsongs);
System.out.println("Response Entity body= "+listofallsongs.getBody().toString());
return "Sucess";
}
Inside config-servlet.xml of my spring MVC app calling the RESTful service
<context:component-scan base-package="com.songs.controllers" />
<mvc:annotation-driven />
<context:annotation-config/>
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"></bean>
<bean class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
Inside SongResource.java of my spring MVC app, which I am trying to use for converting the coming JSON to my SongResource.class object, that my spring MVC app can use in a jsp
//**com.songs.service.resource.SongResource.java**
public class SongResource
{
private String name;
private String film;
private String singer;
public SongResource(String name,String film,String singer)
{
this.name=name;
this.film=film;
this.singer=singer;
}
//setter & getters of all above
}
On calling the spring REST service from my spring MVC app the browser is saying as below-
Error 500--Internal Server Error
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [com.songs.service.resource.SongResource] and content type [application/json]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java :77)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:619)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:446)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:401)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:377)
at com.songs.controllers.FrontSongController.getAllSongs(FrontSongController.java:48)
//and so on
Try this, hope it will help you
#RequestMapping(value="/alls",method=RequestMethod.POST)
public String getAllSongs(ModelMap md)
{
String url="http://localhost:7001/SongAppWS/songappWS/allsongsWS";
RestTemplate rt=new RestTemplate();
SongResource[] songRs = template.getForObject(url, SongResource[].class);
List<SongResource> songs = Arrays.asList(songRs);
md.addAttribute("listname", "Songs available in the repository:");
md.addAttribute("listValues", songs);
return "Sucess";
}
I created execute script rule in alfresco and i kept my javascript file in Company Home>Data Dictionary>script folder.
My Rule is as follows:
All Items > Executes script 'dataFilesScript.js' > Items are created or entered this folder >Finish
1). My Js script is as follows.
var simplehttpresult = "";
try {
simplehttpresult = SimpleHttpConnection.getContentAsString("http://myip:myport/alfresco/service/demo/simple");
}catch(ex){
error = String(ex)
}
2).I add following lines in web-scripts-application-context.xml
<bean id="webscript.org.alfresco.demo.simple.get"
class="org.alfresco.module.demoscripts.SimpleWebScript"
parent="webscript">
<property name="repository" ref="repositoryHelper" />
<property name="serviceRegistry" ref="ServiceRegistry" />
</bean>
3).My simple.get.desc.xml file
<webscript>
<shortname>The World's Simplest Webscript</shortname>
<description>Hands back a little bit of JSON</description>
<url>/demo/simple</url>
<authentication>none</authentication>
<format default="">argument</format>
<family>Alfresco Java-Backed WebScripts Demo</family>
</webscript>
4). I add following lines script-services-context.xml
<bean id="httpUtilsScript" parent="baseJavaScriptExtension"
class="org.um.alfresco.SimpleHttpConnection">
<property name="extensionName">
<value>SimpleHttpConnection</value>
</property>
</bean>
5).My Java file is as follows.
public class SimpleWebScript extends AbstractWebScript
{
public void execute(WebScriptRequest req, WebScriptResponse res)
throws IOException
{
try
{
// build a json object
JSONObject obj = new JSONObject();
// put some data on it
obj.put("field1", "data1");
// build a JSON string and send it back
String jsonString = obj.toString();
res.getWriter().write(jsonString);
}
catch(JSONException e)
{
throw new WebScriptException("Unable to serialize JSON");
}
}
}
.when my rule is executed. It's working fine in Alfresco 4.0 but it is not working for 4.2.c.
In Alfresco 4.2.C My action code was called only once...Any other configuration settings are Required in 4.2.C
Please suggest.....
I'm attempting to create 2 separate web services, both within one spring deployment, both with the wsdl's being generated from the same xsd schemas, yet have them be routed to two separate end points so i can handle the requests differently in the separate contexts.
Ex:
Webservice 1: subset of access, lower privileges and security constraints
Webservice 2: higher privileges
<sws:dynamic-wsdl id="spml-readonly"
portTypeName="SpmlReadOnlyService"
locationUri="SpmlReadOnly">
<sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_core.xsd"/>
</sws:dynamic-wsdl>
<sws:dynamic-wsdl id="spml-crud"
portTypeName="SpmlCrudService"
locationUri="SpmlCrud">
<sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_core.xsd"/>
<sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_search.xsd"/>
<sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_batch.xsd"/>
</sws:dynamic-wsdl>
Now since both wsdls are based off of the same xsds, the 'namespace' and 'localPart" of the requests come across the wire identical, regardless of which web service i'm hitting (/SpmlReadOnly or /SpmlCrud).
Therefore, that's ruling out the deprecated PayloadRootQNameEndpointMapping since the localPart and namespace are still identical, etc,... and my current config simply routes the requests to the same endpoint method handler, and i have no way of distinguishing which web service was called:
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "lookupRequest")
#ResponsePayload
public Source handleLookupRequest(SoapMessage message) throws Exception {
...
}
Is what I'm able to do even possible? If the xsd's are shared and have identical namespaces at the root of the schema, and the same localPart method requests, will there ever be a way to distinguish between them and map to two different end points? Any information on this would be useful! I'm hoping i don't have to set up two separate .wars and deploy them separately with their own code bases on a server!
Thanks,
Damian
You need something that combines URI and PayloadRoot mapping. Unfortunately Spring-Ws doesn't have something like this. But because it's very extensible it's really easy to achieve this.
TL;DR
See This branch at GitHub for working example
Details
You need to create mapping of combined URI+QName to org.springframework.ws.server.endpoint.MethodEndpoint instances. Also you should minimize the code which would duplicate existing Spring-Ws functions.
So 1) You need to explicitly configure Spring-Ws annotations without using <sws:annotation-driven />:
This is your requirement (with my schemas):
<ws:dynamic-wsdl id="spml-readonly" portTypeName="SpmlReadOnlyService" locationUri="SpmlReadOnly">
<ws:xsd location="classpath:springws/model/schema.xsd" />
</ws:dynamic-wsdl>
<ws:dynamic-wsdl id="spml-crud" portTypeName="SpmlCrudService" locationUri="SpmlCrud">
<ws:xsd location="classpath:springws/model/schema.xsd" />
<ws:xsd location="classpath:springws/model/schema2.xsd" />
</ws:dynamic-wsdl>
This is all you need to do by hand which normally is configured by <sws:annotation-driven /> (one adapter with one JAXB marshaller):
<bean class="org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter">
<property name="methodArgumentResolvers">
<list>
<ref local="marshallingPayloadMethodProcessor"/>
</list>
</property>
<property name="methodReturnValueHandlers">
<list>
<ref local="marshallingPayloadMethodProcessor"/>
</list>
</property>
</bean>
<bean id="marshallingPayloadMethodProcessor" class="org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPaths">
<list>
<value>springws.model</value>
</list>
</property>
</bean>
This is custom mapping:
<bean class="springws.PathAndPayloadRootAnnotationEndpointMapping" />
And 2) You should create your own mapping
public class PathAndPayloadRootAnnotationEndpointMapping extends PayloadRootAnnotationMethodEndpointMapping
{
#Override
protected QName getLookupKeyForMessage(MessageContext messageContext) throws Exception
{
String urlPart = "";
QName payloadRootPart = super.getLookupKeyForMessage(messageContext);
TransportContext transportContext = TransportContextHolder.getTransportContext();
if (transportContext != null) {
WebServiceConnection connection = transportContext.getConnection();
if (connection != null && connection instanceof HttpServletConnection) {
String requestURI = ((HttpServletConnection)connection).getHttpServletRequest().getRequestURI();
String contextPath = ((HttpServletConnection)connection).getHttpServletRequest().getContextPath();
urlPart = requestURI.substring(contextPath.length());
}
}
return new QName(payloadRootPart.getNamespaceURI(), urlPart + "/" + payloadRootPart.getLocalPart());
}
#Override
protected List<QName> getLookupKeysForMethod(Method method)
{
List<QName> result = new ArrayList<QName>();
RequestMapping rm = AnnotationUtils.findAnnotation(method.getDeclaringClass(), RequestMapping.class);
String urlPart = rm == null || rm.value().length != 1 ? "" : rm.value()[0];
List<QName> methodPart = super.getLookupKeysForMethod(method);
for (QName qName : methodPart) {
result.add(new QName(qName.getNamespaceURI(), urlPart + "/" + qName.getLocalPart()));
}
return result;
}
}
which extends org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping. And all it does is extending the keys (QNames of payload root elements) of messages with the information extracted from the endpoint URI. I've used Spring's #org.springframework.web.bind.annotation.RequestMapping annotation for that, but someone thinking it's a hack may create his/her own annotation.
So for endpoint like this:
#org.springframework.ws.server.endpoint.annotation.Endpoint
#RequestMapping("/ws/SpmlReadOnly")
public class Endpoint1
{
#ResponsePayload
#PayloadRoot(namespace = "urn:test", localPart = "method1Request")
public Response2 method(#RequestPayload Request1 request) throws Exception
{
return new Response2("e1 m1");
}
}
the key is not:
namespace = urn:test
localName = method1Request
but this:
namespace = urn:test
localName = /ws/SpmlReadOnly/method1Request
The protected QName getLookupKeyForMessage(MessageContext messageContext) method ensures that the mapping URI is independent of the WAR context, the application is deployed at.
I have a web app that acts as a client to a Jax-WS web service implemented using Spring WS. The Spring WS is configured to require a username token in the SOAP header. In the web app I plan to use the Spring web service template but I can't seem to find any examples which show how to add a UsernameToken to the outgoing request.
Can someone point me in the right direction?
Thanks.
You have to use Interceptors. See Chapter 7. Securing your Web services with Spring-WS.
The configuration would be something like this
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
<property name="defaultUri"
value="http://localhost:8080/ws-demo/myws" />
<property name="interceptors">
<list>
<ref bean="wsSecurityInterceptor" />
</list>
</property>
</bean>
<bean id="wsSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="UsernameToken"/>
<property name="securementUsername" value="Ernie"/>
<property name="securementPassword" value="Bert"/>
</bean>
in addition to jddsantaella's answer, the client class can use SAAJ to add username token in the SOAP header:
OrganisationPortfolioRequest request = WS_CLIENT_FACTORY.createOrganisationsPortfolioRequest();
OrganisationPortfolioResponse response;
response = (OrganisationPortfolioResponse) webServiceTemplate.marshalSendAndReceive(request,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
SoapEnvelope envelope = soapMessage.getEnvelope();
envelope.addNamespaceDeclaration("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
envelope.addNamespaceDeclaration("s", "http://company.com/ws/security.xsd");
SoapHeaderElement username = soapMessage.getSoapHeader().addHeaderElement(new QName("http://company.com/ws/security.xsd", "username", "s"));
username.setText(getCurrentUser.getUsername());
}
});
response.getResults();
Above given answer is used for xml.
I mention here annotation base configuration for usernameToken security policy for web service.
Add this configuration with spring boot client
#Bean
public WebServiceTemplate webServiceTemplate() {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller());
webServiceTemplate.setUnmarshaller(marshaller());
webServiceTemplate.setDefaultUri("http://localhost:8080/ws");
webServiceTemplate.setInterceptors(new ClientInterceptor[] {wsSecurityInterceptor()});
return webServiceTemplate;
}
#Bean
public Wss4jSecurityInterceptor wsSecurityInterceptor() {
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
wss4jSecurityInterceptor.setSecurementActions(WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.USERNAME_TOKEN);
wss4jSecurityInterceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
wss4jSecurityInterceptor.setSecurementUsername("user");
wss4jSecurityInterceptor.setSecurementPassword("password");
return wss4jSecurityInterceptor;
}