Using spring integration web service gateways with JMS - spring-ws

I need to implement a webservice client (and after that a webservice server) using spring-integration. I have already worked with the samples given by spring-integration team. That implementation uses HTTP as a transport layer. The client is making a HTTP request and server is sending back HTTP response. Instead of using HTTP I would like to use JMS as a transport layer. In this case client sends a SOAP-Request to a Queue (the server is listening to this queue) and while sending it also creates a temporary Queue and set that in the RepyTo in the JMS message header. Server gets receives the request from the Queue process it and then send back a SOAP-Response using the ReplyTo queue. I know we can do it using spring-ws and spring-jms libraries. I would like to do it using spring-integration support for ws and jms:
client sending request: java object -> Soap Message -> JMS message (payload is the SOAP xml)
server receiving request: JMS message (payload is the SOAP xml) -> Soap Message -> java object
server sending back response: java object -> Soap Message -> JMS message (payload is the SOAP xml)
For example I am giving xml configuration for webservice client that I am trying right now. Can you please check what I am missing?
<bean id="jndiEnvironment" class="java.util.Properties">
<constructor-arg>
<map>
<entry key="java.naming.factory.initial" value="value" />
<entry key="java.naming.provider.url" value="value" />
<entry key="java.naming.security.principal" value="value" />
<entry key="java.naming.security.credentials" value="value" />
</map>
</constructor-arg>
</bean>
<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="/my/jndi/name" />
<property name="jndiEnvironment" ref="jndiEnvironment" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.jibx.JibxMarshaller">
<property name="targetClass" value="zahid.play.si.ws.jms.GetCountryDescriptionRequest" />
</bean>
<bean id="destinationResolver"
class="org.springframework.jms.support.destination.JndiDestinationResolver">
<property name="jndiEnvironment" ref="jndiEnvironment" />
</bean>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />
<bean id="messageSender" class="org.springframework.ws.transport.jms.JmsMessageSender">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destinationResolver" ref="destinationResolver" />
</bean>
<bean id="messageTemplate" class="org.springframework.integration.core.MessagingTemplate">
<property name="defaultChannel" ref="requestChannel" />
</bean>
<int:channel id="requestChannel" />
<ws:outbound-gateway id="wsClientGateway"
uri="jms:MY.TOPIC?messageType=TEXT_MESSAGE&deliveryMode=NON_PERSISTENT"
message-factory="messageFactory" marshaller="marshaller" unmarshaller="marshaller"
message-sender="messageSender"
request-channel="requestChannel" />
In the java code I am using: messagingTemplate.convertSendAndReceive(MessageBuilder.withPayload(request).build()) to send a request.
But I am getting this error:
[jms:MY.TOPIC?messageType=TEXT_MESSAGE&deliveryMode =NON_PERSISTENT] is not a valid HTTP URL

Solved the problem :) Here is the solution:
1) Define a destination provider for your Jms Uri:
public class JmsDestinationProvider implements DestinationProvider {
private String jmsUri;
public URI getDestination() {
if(StringUtils.hasText(jmsUri)){
try {
return new URI(jmsUri);
} catch (URISyntaxException e) {
}
}
return null;
}
public void setJmsUri(String jmsUri) {
this.jmsUri = jmsUri;
}
}
2) In the spring xml file add a bean for this destination provider and use that bean in ws:outbound-gateway
<bean id="jmsDestinationProvider" class="play.zahid.springint.activemq.ws.JmsDestinationProvider">
<property name="jmsUri" value="jms:test_queue?messageType=TEXT_MESSAGE&deliveryMode=NON_PERSISTENT" />
</bean>
<ws:outbound-gateway id="wsClientGateway"
destination-provider="jmsDestinationProvider"
message-factory="messageFactory" marshaller="marshaller" unmarshaller="marshaller"
message-sender="messageSender"
request-channel="requestChannel" />

Related

How to change the CachingConnectionFactory timeout setting

I have a use case where when the connectivity to rabbitmq goes down I need to manage the messages in different way.
I have a logic
RabbitTempate template // get template using API call
template.setReplyTimeout(10000);
template.convertAndSend(message);
But the problem is when the rabbitmq server connectivity goes down Spring AMQP automatically waits for connectivity to get resolved for 5 min
But because of that the client thread calling that particular logic waits for 5 min .
Is there any way i can change it , I am using CachingConnectionFactory
<bean id="connectionFactory"
class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="rabbit-server-fqdn" />
<property name="virtualHost" value="vhost" />
<property name="username" value="username" />
<property name="password" value="password" />
<property name="cacheMode" value="CONNECTION" />
<property name="channelCacheSize" value="25" />
</bean>
Is there any way I can override these settings from 5 min so that client API does not need to wait for such huge amount of time ?

Spring WS "The security token could not be authenticated or authorized"

I'm creating a java client consumer for a web service using SpringWS-Security.
My Request SOAP (That I use in SOAP UI)
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sch="http://myws.mycompany.com/myws/schema">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility">
<wsse:Username>myUsernameString</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">123</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<sch:GetUserDetails idSender="5"/>
</soapenv:Body>
</soapenv:Envelope>
My servlet.xml in the WS.
<bean name="endpointMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="interceptors">
<list>
<ref local="wsSecurityInterceptor" />
</list>
</property>
<bean id="wsSecurityInterceptor"
class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="UsernameToken" />
<property name="validationCallbackHandler" ref="springSecurityCallbackHandler" />
</bean>
<bean id="springSecurityCallbackHandler"
class="org.springframework.ws.soap.security.wss4j.callback.SpringPlainTextPasswordValidationCallbackHandler">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="authenticationProvider" class="ws.security.CustomAuthenticationProviderImpl">
<property name="userCommonService" ref="userCommonService" />
<security:custom-authentication-provider/>
</bean>
<security:authentication-manager alias="authenticationManager" />.
In my Java Client - applicationContext.xml
<bean name="webserviceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="defaultUri" value="http:/localhost:8080/myws-ws/" />
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="unmarshaller" />
<property name="interceptors">
<list>
<ref local="wsSecurityInterceptor" />
</list>
</property>
</bean>
<oxm:jaxb2-marshaller id="marshaller"
contextPath="org.example.bean.schema" />
<oxm:jaxb2-marshaller id="unmarshaller"
contextPath="org.example.org.bean.schema" />
<bean id="client" class="example.client.impl.EfactClientImpl">
<property name="webServiceTemplate" ref="webserviceTemplate" />
</bean>
<bean id="wsSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="UsernameToken"/>
</bean>
When I use SOAP UI to consume the service everything is going fine, I think I need a little help at the Java Client and its context because when I run it I got this error:
The security token could not be authenticated or authorized; nested exception is:
javax.security.auth.callback.UnsupportedCallbackException; nested exception is org.apache.ws.security.WSSecurityException: The security token could not be authenticated or authorized; nested exception is:
javax.security.auth.callback.UnsupportedCallbackException
When I debug my app I can notice that this element is crashing:
GetUserRequest request = new GetUserRequest();
request.setIdentifier(user.getIdentifier());
request.setPassword(user.getPassword());
GetUserResponse response = new GetUserResponse();
/* Crashing here. */
response = (GetUserResponse) getWebServiceTemplate().marshalSendAndReceive(request);
FYI: I always see this list of users in SpringWS with security, but what if I have a lot of users trying to access.
WS - [servlet-name]-servlet.xml
<bean id="callbackHandler" class="org.springframework.ws.soap.security.wss4j.callback.SimplePasswordValidationCallbackHandler">
<property name="users">
<props>
<prop key="Bert">Ernie</prop>
<prop key="Mickey">Mouse</prop>
</props>
</property>
</bean>
How can I resolve this UnsupportedCallbackException Exception?
You must specify SecurementUsername and SecurementPassword at call time. This is a programmatically example:
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setDefaultUri(uri);
Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();
interceptor.setSecurementActions(securementActions);
interceptor.setSecurementMustUnderstand(true);
interceptor.setSecurementUsername(usuario);
interceptor.setSecurementPassword(contrasena);
webServiceTemplate.setInterceptors(new ClientInterceptor[] {interceptor});
I was getting this error when I upgraded from cfx 2.2.X to 2.7.X. Due to feature upgrades, the server side code was not able to read the password as a result the password was received as null by the server. Make sure the server received the correct username and password, which would fix this issue.

Transforming spring-ws implementation to spring-integration-ws implementation

I have an implementation of web service (server side) using spring-ws. I would like to change my code to use spring-integration-ws (ws:inbound-gateway and channels). I have tried examples but still not sure if it is possible to transform my implementation. In short my I am using #Endpoint, #PayloadRoot, PayloadRootAnnotationMethodEndpointMapping and JMS (as transport). Please find below my implementation:
The endpoint class (MyEndpoint.java):
#Endpoint
public class MyEndpoint {
#PayloadRoot(localPart="getUserRequest", namespace="http://play.zahid.springint/Jibx")
public GetUserResponse getUserResponse(GetUserRequest request){
log.info("Start getUserResponse " + request);
GetUserResponse response = new GetUserResponse();
User user = new User();
user.setFirstName(request.getFirstName());
user.setLastName(request.getFirstName() + " Last");
response.setUser(user);
log.info("End getUserResponse " + response);
return response;
}
#PayloadRoot(localPart="sayHiUserRequest", namespace="http://play.zahid.springint/Jibx")
public SayHiResponse sayHiUserResponse(SayHiUserRequest request){
log.info("Start sayHiUserResponse " + request);
SayHiUserResponse response = new SayHiUserResponse();
response.setResponse("Hi " + request.getFirstName() + " " + request.getLastName());
log.info("End sayHiUserResponse " + response);
return response;
}
}
The spring xml file (applicationContext.xml):
<bean id="listenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="mqCachedConnectionFactory" />
<property name="destination" ref="defaultDestination" />
<property name="messageListener">
<bean class="org.springframework.ws.transport.jms.WebServiceMessageListener">
<property name="messageFactory" ref="messageFactory"/>
<property name="messageReceiver" ref="messageReceiver" />
</bean>
</property>
<property name="concurrentConsumers" value="1" />
<property name="acceptMessagesWhileStopping" value="false" />
<property name="recoveryInterval" value="10000" />
<property name="cacheLevelName" value="CACHE_CONSUMER" />
</bean>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />
<bean id="messageReceiver" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointAdapters">
<list>
<bean id="endpointAdapter" class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter">
<constructor-arg ref="marshaller" />
</bean>
</list>
</property>
</bean>
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping" />
For simplicity purpose I have removed the unnecessary codes and xml configuration.
Is there anyway to keep my endpoint class (MyEndpoint.java) as it is and use spring-integration-ws support (ws:inbound-gateway and channel)?
Let me know if you need any more information.
Thanks
Is there anyway to keep my endpoint class (MyEndpoint.java) as it is and use spring-integration-ws support (ws:inbound-gateway and channel)?
Yes you can.
I'll try to explain a simple integration-ws implementation for your situation:
applicationContext.xml:
<bean id="endpointMappings"
class="org.springframework.beans.factory.config.ListFactoryBean"
lazy-init="true">
<property name="sourceList">
<list>
<ref bean="uriEndpointMapping"/>
</list>
</property>
</bean>
<bean id="uriEndpointMapping"
class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping"
p:defaultEndpoint-ref="ws-inbound-gateway">
<property name="mappings">
<props>
<prop key="${yourEndPointUrl}">ws-inbound-gateway</prop>
</props>
</property>
</bean>
<int-ws:inbound-gateway id="ws-inbound-gateway"
request-channel="ws-request-channel"
reply-channel="ws-response-channel"
error-channel="ws-error-channel"
marshaller="marshaller"
unmarshaller="marshaller"/>
<int:channel id="ws-request-channel"/>
<int:channel id="ws-response-channel"/>
<int:channel id="ws-error-channel"/>
<int:chain input-channel="ws-request-channel" output-channel="ws-response-channel">
<int:service-activator ref="yourBean" method="getUserResponse"/>
</int:chain>
There are some points here.
You need to define a bean, MyEndpoint type, I named it yourBean here.
yourEndPointUrl defines your end point URL for getUserResponse method.
I've defined service-activator inside a chain, you can add logging or another thing inside this chain to enrich your handling mechanism.
You also need to have a router for routing your requests in to different methods of your bean.

Spring ws XSD validation

Currently i am implementing web services using Spring-ws . Here i am struck with xsd validation . For xsd validation i am using the following configruation
<bean id="validatingInterceptor" class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="xsdSchema" ref="schema" />
<property name="validateRequest" value="true" />
<property name="validateResponse" value="true" />
</bean>
<bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="/WEB-INF/ProductSchema.xsd" />
</bean>
Here i am passing the xsd file during bean initialization . Is there any way for me to send this(ProductSchema.xsd) xsd file dynamically. Because I will comes to know which xsd file needs to send based on the input payload.
Please help me. Thanks in advance
I don't know how many XSD's you have, but perhaps you can define imports in ProductSchema.xsd to include the others. That's at least how I've got it set up.
For example:
<import namespace="http://namespace" schemaLocation="data.xsd" />
I'm not quite sure of what you are trying to do.
But you can make different endpoints/methods that matches different payloads by annotating the handler method with a localPart that matches the name of an element i the payload:
#Endpoint
public class MyEndpoint {
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "NameOfMyXmlRequestElement")
#ResponsePayload
public MyResponse handleMyRequest(#RequestPayload MyRequest MyRequest) throws Exception {
...
A recived request can then be unmarshalled/validated using a specific schema:
<bean id="myJaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>mydomain.model.oxm.MyRequest</value>
<value>mydomain.model.oxm.MyResponse</value>
</list>
</property>
<property name="schema" ref="MyServiceSchema" />
</bean>
<bean id="MyServiceSchema" class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="WEB-INF/schemas/MyService.xsd" />
</bean>
The MyRequest class must be annotated to work with the Jaxb2marshaller, #XmlRootElement(name="MyRequest") etc...

Creating a custom authentication with Acegi/Spring Security

I'm having trouble discovering exactly what I need to implement in order to use a custom authentication method with my web application using Spring Security. I have a Grails application with the Spring Security plugin that currently uses the standard user/password authentication with a browser form. This is working correctly.
I need to implement a mechanism alongside of this that implements a type of MAC authentication. If the HTTP request contains several parameters (e.g. a user identifier, timestamp, signature, etc.) I need to take those parameters, perform some hashing and signature/timestamp comparisons, and then authenticate the user.
I'm not 100% sure where to start with this. What Spring Security classes do I need to extend/implement? I have read the Reference Documentation and have an okay understanding of the concepts, but am not really sure if I need a Filter or Provider or Manager, or where/how exactly to create Authentication objects. I've messed around trying to extend AbstractProcessingFilter and/or implement AuthenticationProvider, but I just get caught up understanding how I make them all play nicely.
Implement a custom AuthenticationProvider which gets all your authentication information from the Authentication: getCredentials(), getDetails(), and getPrincipal().
Tie it into your Spring Security authentication mechanism using the following configuration snippet:
<bean id="myAuthenticationProvider" class="com.example.MyAuthenticationProvider">
<security:custom-authentication-provider />
</bean>
This step is optional, if you can find a suitable one from standard implementations. If not, implement a class extending the Authentication interface on which you can put your authentication parameters:
(e.g. a user identifier, timestamp, signature, etc.)
Extend a custom SpringSecurityFilter which ties the above two classes together. For example, the Filter might get the AuthenticationManager and call authenticate() using your implementation of Authentication as input.
You can extend AbstractAuthenticationProcessingFilter as a start.
You can reference UsernamePasswordAuthenticationFilter which extends AbstractAuthenticationProcessingFilter. UsernamePasswordAuthenticationFilter implements the standard Username/Password Authentication.
Configure your Spring Security to add or replace the standard AUTHENTICATION_PROCESSING_FILTER. For Spring Security Filter orders, see http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html#filter-stack
Here is a configuration snippet for how to replace it with your implementation:
<beans:bean id="myFilter" class="com.example.MyAuthenticationFilter">
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
</beans:bean>
I have recently put up a sample application that does custom authentication with Spring Security 3.
The source code is here.
More details are in this blog post.
Here is an example of securityContext.xml configuration file using custom autenticationFilter (extending AUTHENTICATION_PROCESSING_FILTER) and authenticationProvider. The user authentication data is provided by jdbc connection. Configuration is for Spring Security 2.0.x
<?xml version="1.0" encoding="UTF-8"?>
<sec:global-method-security />
<sec:http auto-config="false" realm="CUSTOM" create-session="always" servlet-api-provision="true"
entry-point-ref="authenticationProcessingFilterEntryPoint" access-denied-page="/notauthorized.xhtml"
session-fixation-protection="migrateSession">
<sec:port-mappings>
<sec:port-mapping http="80" https="443" />
</sec:port-mappings>
<sec:anonymous granted-authority="ROLE_ANONYMOUS" username="Anonymous" />
<sec:intercept-url pattern="/**" access="ROLE_ANONYMOUS, ROLE_USER" />
<sec:logout logout-url="/logoff" logout-success-url="/home.xhtml" invalidate-session="false" />
</sec:http>
<bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.xhtml" />
<property name="forceHttps" value="false" />
</bean>
<bean id="authenticationProcessingFilter" class="mypackage.CustomAuthenticationProcessingFilter">
<sec:custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
<property name="defaultTargetUrl" value="/" />
<property name="filterProcessesUrl" value="/logon" />
<property name="authenticationFailureUrl" value="/loginError.xhtml" />
<property name="alwaysUseDefaultTargetUrl" value="false" />
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<jee:jndi-lookup id="securityDataSource" jndi-name="jdbc/DB_DS" />
<bean id="myUserDetailsService" class="mypackage.CustomJdbcDaoImpl">
<property name="dataSource" ref="securityDataSource" />
<property name="rolePrefix" value="ROLE_" />
</bean>
<bean id="apcAuthenticationProvider" class="mypackage.CustomDaoAuthenticationProvider">
<property name="userDetailsService" ref="myUserDetailsService" />
<sec:custom-authentication-provider />
</bean>
<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<list>
<ref local="apcAuthenticationProvider" />
</list>
</property>
</bean>
</beans>

Resources