Converting Spring xml config to java - java

I've an XML config for mail service in spring, that I'd like to transform to JAVA config.
XML Looks like this
<int:chain id="chain"
input-channel="outMailError"
output-channel="outMailEntry">
<int:poller max-messages-per-poll="1" fixed-rate="20000" />
<int:transformer ref="mailSendErrorTransformer" />
</int:chain>
<int:channel id="outMailError">
<int:queue capacity="500" />
</int:channel>
<int:channel id="outboundMailEntry" />
I was able to convert the channels to
#Bean
public DirectChannel outboundMailEntry() {
return new DirectChannel();
}
#Bean
public QueueChannel outboundMailErrorChannel() {
return new QueueChannel(500);
}
But I don't know how to the same for int:chain. I was able to debug and find out what type of bean does spring instantiate for "chain" part of xml - it is PollingConsumer that takes 2 params PollableChannel inputChannel, MessageHandler handler.
The first one is not a problem since I already have that, it is the
#Qualifier("outMailError")
QueueChannel channel
But I do not know about the second one... Spring itself initializes some MessageHandlerChain but I was unable to to set the outMailEntry to it and also dont know about the poller and transformer.. any ídeas?

There is no chain equivalent in Java Config. It was designed especially for XML to let to minimize XML coding.
On the other hand it doesn't look like you need that <chain> at all: you have only one <int:transformer> over there.
In Java Config you would use a #Trasnoformer annotation on your mailSendErrorTransformer method with appropriate inputChannel and outputChannel attributes. The <int:poller> equivalent is also present over there as a poller attribute with resective #Poller configuration.
See more info in the docs starting from here: https://docs.spring.io/spring-integration/docs/current/reference/html/overview.html#configuration-enable-integration

Related

Interceptor for not existing request mapping with java configuration

I have a configuration class, which extends WebMvcConfigurationSupport and I have added interceptors like this:
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor()).addPathPatterns("/api/**");
}
where myInterceptor() is:
#Bean
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}
and it works fine for any mapping (/api/**) which I have implemented for example /api/sample - preHandle from MyInterceptor is fired (I have a Controller with mapping /api/sample).
When I call not existing resource for example /api/forward/sample preHandle from MyInterceptor is never called.
Please notice it worked as expected when I had the config in xml, like:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/api/**" />
<bean class="my.pack.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
For some reason with java configuration requests for not existing mappings are not intercepted. Why the configuration is not equivalent? I thought it should be.
EDIT:
A bit more debugging information. With xml configuration DispatcherServlet#handlerMappings contains 5 handlers:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.socket.server.support.WebSocketHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
with Java configuration it contains 7 handlers:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.socket.server.support.WebSocketHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
The problem seems to be with SimpleUrlHandlerMapping (at least it seems to be used for the resource I call - api/forward/sample while for api/sample RequestMappingHandlerMapping is in use) which has empty adoptedIterceptors in the case with Java based configuration.
EDIT 2:
Full source code for sample application (I tried to make it as small as possible just to demonstrate the effect):
https://github.com/szprutamich/spring-demo
In class ConfigurationBase - configuration can be switched from xml based to java based with static field CONFIG.
With xml based config both urls work:
/api/sample
/api/forward/sample
With java based config forward does not work.
Your question is about a "not existing request mapping", but in your XML configuration, it exists :
<default-servlet-handler xmlns="http://www.springframework.org/schema/mvc" />
This declares a default handler for all requests, and a request interceptor ony works if a valid handler is found. Remove this line and you will get the same behavior in both XML and Java configs : no default handler will be found and the interceptor won't work.
So for the interceptor to work on all requests in your Java configuration, you have to declare a default handler. You could override configureDefaultServletHandling but afaik it's not possible to configure interceptors on it. You may have to explicitly map /** in a default handling controller.

Multiple service activators of same input channel in spring integration

I have a requirement of processing the same information of a request in two different ways asynchronously. I am using spring integration in my project.
Can I have two service activators reading from the same input channel as below? I will get my data from a queue through an adapter and forwarded to the channel.
<int:channel id="dataChannel" />
<service-activator input-channel="dataChannel" ref="serviceA" method="method1">
<service-activator input-channel="dataChannel" ref="serviceB" method="method2">
<bean id="serviceA" class="a.b.test.ServiceA">
<bean id="serviceB" class="a.b.test.ServiceB">
Change dataChannel to
<int:publish-subscribe-channel id=`dataChannel` task-executor="exec" />
(declare a <task:executor ... />).

Java DSL Equivalent of Spring Integration Kafka Endpoint Configured in XML

I have the following XML configuration for a Kafka outbound channel adapter:
<int-kafka:outbound-channel-adapter id="kafkaOutboundChannelAdapter"
kafka-producer-context-ref="kafkaProducerContext"
auto-startup="true"
channel="activityOutputChannel">
<int:poller fixed-delay="1000" time-unit="MILLISECONDS" receive-timeout="0" task-executor="taskExecutor"/>
</int-kafka:outbound-channel-adapter>
<task:executor id="taskExecutor"
pool-size="5-25"
queue-capacity="20"
keep-alive="120"/>
This works just fine. I am trying to replicate this in the Java DSL, but I can't get too far. So far, I just have this:
.handle(Kafka.outboundChannelAdapter(kafkaConfig)
.addProducer(producerMetadata, brokerAddress)
.get());
I can't figure out how to add the taskExecutor and the poller with the DSL.
Any insight on how to incorporate these into my overall IntegrationFlow is appreciated.
The Spring Integration components (e.g. <int-kafka:outbound-channel-adapter>) consist with two beans: AbstractEndpoint to accept messages from the input-channel and MessageHandler to handle message.
So, Kafka.outboundChannelAdapter() is about MessageHandler. Any other endpoint-specific properties are up to the second Consumer<GenericEndpointSpec<H>> endpointConfigurer argument of .handle() EIP-method:
.handle(Kafka.outboundChannelAdapter(kafkaConfig)
.addProducer(producerMetadata, brokerAddress),
e -> e.id("kafkaOutboundChannelAdapter")
.poller(p -> p.fixedDelay(1000, TimeUnit.MILLISECONDS)
.receiveTimeout(0)
.taskExecutor(this.taskExecutor)));
See Reference Manual for more information.

How to create channels with Spring 4 annotation based?

I want to create a socket channel with a rendezvous queue where a client and server can exchange a simple message.
But I already fail to convert the following xml to annotation based spring-4 configuration:
<int:channel id="rendezvousChannel"/>
<int:rendezvous-queue/>
</int:channel>
How would this look like in spring 4?
The short answer:
#Bean
public PollableChannel rendezvousChannel() {
return new RendezvousChannel();
}
Can you explain why it was an issue for you to find the solution from your side?
Any XML component is backed by some Java class anyway and most cases their names reflect the XML component names.

Convert SOAP to a virtual Restful service?

Is there a way to use my SOAP web service(spring-ws, java) as a XML based RESTful service virtually?
I don't want to re-write whole my SOAP web service into RESTful from scratch in java, but I need to access it through iphone using REST, which they already have easy native support.
XMLGateway, Proxys..? or some extra java code? since my SOAP request and response is simply an XML file why can't I modify it to be used by a REST service?
Or without changing changing any logic and xml parsing in my application is that easy to add jax-rs annotations and create a rest request xml?
my config spring file is like this:
<bean id="webServicePluginDescriptor"
class="com.mysite.ws.configuration.MyWebservicePluginDescriptor" />
<bean id="payloadMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="defaultEndpoint" ref="inferenceEndPoint" />
<property name="interceptors">
<list>
<ref local="validatingInterceptor" />
<ref local="payLoadInterceptor" />
</list>
</property>
</bean>
<bean id="payLoadInterceptor"
class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor" />
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="schema"
value="classpath:/wsdl/Request.xsd" />
<property name="validateRequest" value="true" />
<property name="validateResponse" value="false" />
</bean>
<bean id="PropertyResource" class="com.mysite.ws.im.PropertyResource">
<property name="resource"
value="/WEB-INF/client-specific/InferenceMachine.properties" />
</bean>
<bean id="inferenceEndPoint" class="com.mysite.ws.web.InferenceEndPoint">
<property name="messageWebService" ref="messageWebService" />
</bean>
<bean id="messageWebService" class="com.mysite.ws.service.MessageWebService"
scope="request">
<aop:scoped-proxy />
<property name="inferenceService" ref="inferenceService" />
</bean>
<bean id="Request" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="classpath:/wsdl/Request.xsd" />
</bean>
<bean id="Response" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="classpath:/wsdl/Response.xsd" />
</bean>
<bean id="Error" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="classpath:/wsdl/Error.xsd" />
</bean>
<bean id="mwsid"
class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
<constructor-arg value="classpath:/wsdl/mtchwsdl.wsdl" />
</bean>
<bean id="inferenceService" class="com.mysite.ws.im.InferenceService"
scope="request">
<aop:scoped-proxy />
<property name="webServiceConfiguration" ref="wsPlayerConfiguration" />
<property name="properties">
<bean class="com.mysite.ws.im.PropertyResource">
<property name="resource"
value="/WEB-INF/client-specific/InferenceMachine.properties" />
</bean>
</property>
</bean>
<!-- ~~~~~~~ Application beans ~~~~~~~ -->
<bean id="wsPlayerConfiguration"
class="com.mysite.ws.configuration.WebServiceConfiguration"
scope="request">
<aop:scoped-proxy />
<property name="playerConfiguration" ref="playerConfiguration"></property>
<property name="configurationSetup" ref="configurationSetup"></property>
</bean>
and this is my endpoint class:
/**
* The EndPoint of the Web Service Application. This class gets the raw
* SOAP-body message from the Spring Payload Dispatcher and sends the message to
* the #see MessageService class. After it has gotten the response XML message
* it returns this back to the Spring Payload Dispatcher.
*/
public class InferenceEndPoint extends AbstractJDomPayloadEndpoint {
private MessageWebService messageWebService;
public InferenceEndPoint() {
}
#Override
protected Element invokeInternal(Element inferenceRequest) throws Exception {
Element ret = messageWebService.handleRequest(inferenceRequest);
return ret;
}
/**
* #param messageWebService
*/
public void setMessageWebService(MessageWebService messageWebService) {
this.messageWebService = messageWebService;
}
}
any ideas?
Spring-WS just adds some annotations to your beans and then you have Spring beans doing most of the heavy lifting. Presumably you have some classes annotated with #Endpoint, #PayloadRoot and the like. You should be able to re-use all of these in one of three ways.
If your Spring-WS endpoint classes are adapter pattern style (e.g. your Endpoint class is injected with a POJO service that does the real work), then you could do a similar adapter-style Spring MVC Controller (where REST exists in Spring 3.0).
If you have your annotations directly on your business logic class, then in theory, you should be able to just sprinkle on some more annotations (might get a little busy-looking).
If you have Spring-WS beans that are POX (and not SOAP), then you might be able to get away with some fancy URL mappings to give them more RESTful looking URLs
To migrate to the Spring 3 for REST support, add the appropriate #RequestMapping and another annotations to expose them as REST services to match specific URLs. While you're adding, you might as well remove the old #PayloadRoots and #Endpoint, but that may not be a big deal. Of course if you leave old Spring-WS annotations around, you'll still need the Spring-WS jars on your classpath, but as long as you are not using a Spring-WS servlet nor any other other beans in your Spring file - you should be okay (in theory...).
The biggest gotchas will be:
Don't forget to remove Spring-WS beans from your Spring file
DO remember to add the Spring MVC beans to your Spring file, and most importantly different Dispatcher servlet
REST security in Spring will be provided by Spring Security, not the same SOAP interceptors found in Spring-WS, so that will be a complete overhaul. The good news is that Spring Security is actually pretty easy to work with
Looking at your code, it's very difficult to tell what the best approach would be. REST and SOAP are really quite different ways of imagining how a web-based service interface could work: SOAP is all about method calls, and REST is all about resources, representations and links. To convert, you have to start from your underlying abstract API.
If your basic API is one of “I give you a document, you give me a response document” and there's no exposure of anything other than that, it's a very SOAP-oriented model. You can model that in REST through POSTing a document and getting a response, but it's not at all elegant. If you can instead think of your interface in terms of “here's an overall resource, with properties I can set, and certain operations I can do” then that maps to REST far more easily (the overall resource is represented as a document full of links to individual property resources and operations, and the individual properties can be GET and PUT – and possibly DELETEd – as necessary). Which style you've got… well, it looks a lot like you've got the first, but I'm only guessing because determining it for real would require looking at more of your code than you've shown.
There's nothing stopping you from send POST filled with XML to get results filled with XML back.
The simplest thing to do is somehow capture the SOAP requests going back and forth, and simply turn the request in to a template with the blanks being your parameters, and then using XPath on the resulting XML to pull your results out.
The only nit being you might need SOAPAction header in your POST, but likely not.
It's really no big deal. If you have dozens of methods, it's more of a pain. Also, if you're using any of the encryption parts of SOAP, then it's more of a pain. But if just a few, in the end it's just XML, and a bulk of that XML is boilerplate, so looked at that way, it's pretty straightforward.
Addenda:
If you have back end logic that you want to front with a more HTTP friendly service, then JAX-RS can to that quite easily, but it would require coding on the server end.
If you have an existing SOAP web service that you wish to use, then forget the whole SOAP part of the equation, and simply treat it as an HTTP web service that uses XML payloads. It's still SOAP, but you're not using any SOAP tooling on the client side. You're simply assembling XML payloads on the client requests (from templates would be easiest, IMHO), and consuming XML payloads as a result, and trading these over HTTP.
Depending on how many different methods on the existing web service you intend to call gives you an idea of the scope of the work involved. It's simple work (once you can view the payloads easily), but it's still work. If you only have a few methods, especially if the interface is stable and not changing, it's far easier to just work with the raw XML than learn and fight some new unfamiliar framework.
This is how I solved that problem, using Spring Boot + Spring Integration.
Having a WSDL of the SOAP WS, I used maven-jaxb2-plugin to have my Java POJO's generated at build time.
Optionally, you could create transformations to adapt those classes and attributes to what the REST client expected.
Using Spring Integration, I mapped every REST endpoint to a SOAP gateway like this:
#Bean
public IntegrationFlow myFlow(GenericTransformer reqTransformer, GenericTransformer resTransformer) {
return IntegrationFlows
.from(this.getRestGateway(POST, "/api/entity", MyRestResponse.class))
.transform(reqTransformer)
.handle(this.getSoapGateway("gwBean"))
.enrichHeaders(h -> h.header(HttpHeaders.STATUS_CODE, HttpStatus.OK))
.transform(resTransformer)
.logAndReply();
}
private HttpRequestHandlingMessagingGateway getRestGateway(HttpMethod method, String path, Class payloadType) {
HttpRequestHandlingMessagingGateway httpGateway = new HttpRequestHandlingMessagingGateway();
RequestMapping requestMapping = new RequestMapping();
requestMapping.setMethods(method);
requestMapping.setPathPatterns(path);
httpGateway.setRequestMapping(requestMapping);
httpGateway.setReplyTimeout(timeout);
httpGateway.setRequestPayloadTypeClass(payloadType);
httpGateway.setMessageConverters(asList(jsonConverter));
return httpGateway;
}
private MarshallingWebServiceOutboundGateway getSoapGateway(String nameBean) {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath(generatedClassesPackage);
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
messageFactory.afterPropertiesSet();
MarshallingWebServiceOutboundGateway webServiceGateway = new MarshallingWebServiceOutboundGateway(soapUri, marshaller);
webServiceGateway.setMessageFactory(messageFactory);
webServiceGateway.setBeanName(nombreBean);
return webServiceGateway;
}

Categories