I've started learning the Apache CXF with Spring. First of all, I've tried to create a simple client/server model.
The server-side is:
service.HelloWorld.java
#WebService
public interface HelloWorld {
String sayHi(String text);
}
service.HelloWorldImpl.java
#WebService(endpointInterface = "service.HelloWorld")
public class HelloWorldImpl implements HelloWorld {
public String sayHi(String text) {
return "Hello, " + text;
}
}
The client-side is:
client.Client.java
public class Client {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"cxf-client-servlet.xml"});
HelloWorld client = (HelloWorld) context.getBean("client");
System.out.println(client.sayHi("Batman"));
}
}
cxf-client-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schema/jaxws.xsd">
<bean id="client" class="service.HelloWorld" factory-bean="clientFactory" factory-method="create"/>
<bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
<property name="serviceClass" value="service.HelloWorld"/>
<property name="address" value="http://localhost:8080/services/HelloWorld"/>
</bean>
The problem is: to make the client work I've had to add service.HelloWorld (package + interface) to the clients's project. I've heard that before using a service I need to generate a stub. So it's confusing for me. So that, what is the correct approach and what is the best practice (may be it is better to use some contract-first approach or suchlike)? Later, I want to add WS-Security, so I need a strong background =)
Thanks in advance.
You can use a simple spring configuration like this for client side -
<jaxws:client id="mywebServiceClient"
serviceClass="com.saurzcode.TestService"
address="http://saurzcode.com:8088/mockTestService">
<jaxws:binding>
<soap:soapBinding version="1.2" mtomEnabled="true" />
</jaxws:binding>
</jaxws:client>
<cxf:bus>
<cxf:outInterceptors>
<bean class="com.saurzcode.ws.caller.SoapHeaderInterceptor" />
</cxf:outInterceptors>
</cxf:bus>
Ignore the Interceptor if you don't need it.
More details in this post.
If you are doing code-first WS development then it is fine to distribute the interface and give it to the client. I believe #WebService is not needed (?) on the interface (only implementation), so the client does not have dependencies on this annotation.
Even if you are doing code-first web-services, you may still download the WSDL document generated for you by Apache CXF and give it to the client instead. With this approach (which is considered more mature, not to mention it can be used on different platforms like .NET) the client has to generate the stubs (using tool like wsdl2java). This process will essentially create very similar client interface automatically.
That's one of the reasons why so many people prefer contract-first development - the same WSDL is used to generate client-side stubs and server-side WS implementation. This limits the scope of (accidental) incompatibilites.
Related
In the spring reference documentation
section 2.3 Usage scenarios, there is a paragraph that goes like this
Sometimes circumstances do not allow you to completely switch to a different framework. The Spring
Framework does
not
force you to use everything within it; it is not an
all-or-nothing
solution. Existing
front-ends built with Struts, Tapestry, JSF or other UI frameworks can be integrated with a Spring-
based middle-tier, which allows you to use Spring transaction features. You simply need to wire up your
business logic using an ApplicationContext
and use a WebApplicationContext
to integrate
your web layer.
Now I am not able to understand the last sentence. How can we wire up our business logic using an ApplicationContext and use a WebApplicationContext to integrate with web layer. How can we achieve this? And is the web-layer that they are talking about contains controllers and jsps?
As far as I remember if we needed any object in a class we simply autowire them and spring does the rest of the work.
Can someone please provide an explanation with examples. Please forgive my ignorances as I have just started to learn spring.
If a similar question is asked can someone please point me in the right direction
It is possible, even you can create more than one different context hierarchically.
I will give both answers, both hierarchic and non-hierarchic. I'll use java based configuration for both. I will give the answer for two context but you can implement this for many context.
1)Non-Hierarchic
Create two different context.xml, assume that context1.xml and context2.xml . context1.xml should be like this :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=..... some imports >
<context:annotation-config />
<context:component-scan base-package="desiredPackage1" />
<bean id="properties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>db.properties</value>
</list>
</property>
</bean>
<context:property-placeholder properties-ref="properties"/>
For context2.xml change only
<context:component-scan base-package="desiredPackage2" />
Then create a Configuration.java class like this:
public class Config {
public static void main(String[] args) throws Exception {
ApplicationContext desiredContext1 = new ClassPathXmlApplicationContext("file:////...path.../context1.xml");
ApplicationContext desiredContext2 = new ClassPathXmlApplicationContext("file:////...path.../context2.xml");
}
}
Now you have two different context, if you want to it hierarchically, change the main method like this :
2)Hierarchic
public class Config {
public static void main(String[] args) throws Exception {
ApplicationContext desiredContext1 = new ClassPathXmlApplicationContext("file:////...path.../context1.xml");
String[] congigPath = new String[1];
congigPath[0] = "file:////...path.../context2.xml";
ApplicationContext desiredContext2 = new ClassPathXmlApplicationContext(congigPath,desiredContext1);
}
}
In this case, desiredContext2 object could see desiredContext1 object but desiredContext1 object can not see desiredContext2 object.
If you want to use it when building your web-app use this annotations with you configuration class,
#Configuration
#ImportResource("context1.xml", "context2.xml")
public class Config { ....
I hope this will help to you.
You may setup two or even three different projects or modules each with their own context. For example a web project with WebApplicationContext which is rendering the views and calls business methods i.e. usinf restful web services from business tier. And setup a separate project or module to handle business which has its own context file and beans. And even a commons project to include shared beans between the web and business tier.
I want to combine Akka, Apache Camel, Spring and do not know the way forward for leveraging the three things in the same project.
I was successfully able to
1. write some working code with akka, akka-camel extension and camel routes(Java DSL)
2. use camel and spring (use java DSL but spring for transactions and etc..)
Now I need to combine 1 and 2. Can anyone suggest me the simplest way to achieve this?
EDIT
Some say AKKA no longer supports Spring due to conflict in object instantiation as per the link below
Why spring integration doc for akka exists only for 1.3.1 but not for next versions
Also a similar question is there without a proper solution being presented but the post is about 2 years old
akka-camel 2.2.1 route definition using Spring XML
In one blog post (which I can't get hold of the link right now) a method has been described which is in summary, the actors are defined and used Akka way and what ever the processing Akka actors does to be wired using Spring. But there wasn't any solid example.
I imagine your #2 looks like this:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ctx="http://www.springframework.org/schema/context"
xmlns:camel="http://camel.apache.org/schema/spring"
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://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd >
<!-- Camel route configuration -->
<camelContext id = "testCamelRouteContext" xmlns="http://camel.apache.org/schema/spring">
<route id="test_data_webservice">
<from uri="jetty:http://localhost:8888/myTestService"/>
<log logName="HTTP LOG" loggingLevel="INFO" message="HTTP REQUEST: ${in.header.testdata}"/>
<process ref="myTestService"/>
</route>
</camelContext>
<context:annotation-config />
<bean class="com.package.service" id="myTestService"/>
<bean id="genericDao" class="com.dao.Impl">
<property name="dataSource" ref="datasource" />
</bean>
<bean id="testingDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
datasource stuff
</bean>
</beans>
Is it possible that you can get this camel context through Akka? Something like.
Add in your Akka config:
akka.camel.context-provider="myNewContext"
New ContextProvider class:
class myNewContext extends ContextProvider{
override def getContext(system: ExtendedActorSystem): SpringCamelHybridContext
}
I am guessing this is where the bean injection collision between Spring and Akka could occur. I have never used Akka before so my answer is trivial but I wanted to see if I could provide some help to you.
Reviving an old thread.
akka-springctx-camel library is there to make your life painless to integrate Akka, Spring, Camel, CXF etc.
Add artifact :
<dependency>
<groupId>com.github.PuspenduBanerjee</groupId>
<artifactId>akka-springctx-camel</artifactId>
<version>1.0.0</version>
</dependency>
Add Camel Context Provider in Akka config:
akka.camel.context-provider=system.SpringCamelContextProvider
Get hold of ActorSystem :
implicit val system = SpringContextActorSystemProvider.create
Create a custom RouteBuilder[other way could be Akka Consumer]
class CustomRouteBuilder(system: ActorSystem, echoActor: ActorRef)
extends RouteBuilder {
def configure {
from("direct:testEP")
.routeId("test-route")
.to(echoActor)
}
Get Camel(Spring) Context and add routes to it:
val camel = CamelExtension(system)
camel.context.addRoutes(
new CustomRouteBuilder(system, system.actorOf(Props[EchoActor])))
This test case will give you a detailed idea: https://github.com/PuspenduBanerjee/akka-springctx-camel/blob/master/src/test/scala/AkkaSpringCtxTestSpec.scala
I am using Spring 4 + Websockets + Stomp JS library.
I could not find any way to setup websocket ping/pong mechanism (heartbeat).
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans" ...">
<websocket:message-broker>
<websocket:stomp-endpoint path="/cors/auth/clientEndpoint">
<websocket:handshake-handler ref="myHandshakeHandler" />
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/queue, /topic" />
<websocket:client-inbound-channel>
<websocket:interceptors>
<bean class="com.mycompany.myproject.utils.messaging.MyInboundChannelInterception"></bean>
</websocket:interceptors>
</websocket:client-inbound-channel>
</websocket:message-broker>
<bean id="myHandshakeHandler" class="com.mycompany.myproject.utils.security.MyHandshakeHandler" />
<bean class="org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean">
<property name="maxSessionIdleTimeout" value="120000" />
</bean>
As result, I am implementing my own mechanism of ping/pong messages.
One of the tasks here - to implement server side closure of the websocket in case if no ping message during more than 10s from client.
And no way to do this using Spring Websockets!
Maybe somebody can tell me how to access Session object of the user or to close those Session via Spring Websockets?
Seems Spring is very limited here.
I'm surprised spring doc doesn't mention how to config server ping...It seems that spring expects us to read code instead of read doc..
after some time searching on net and reading source code, I realize it's already supported, but not documented at a noticeable place like spring websocket doc.
I'm using spring 4.3.3, and here is how to config server ping without using sockJS:
#Configuration
#EnableWebSocketMessageBroker
public class StompOverWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins("*");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
ThreadPoolTaskScheduler pingScheduler = new ThreadPoolTaskScheduler();
pingScheduler.initialize();
registry.enableSimpleBroker("/topic")
.setHeartbeatValue(new long[]{20000, 0}).setTaskScheduler(pingScheduler);
}
....
}
and should make sure that you correctly set web socket session timeout, it should be greater than ping interval, like this:
<bean id="servletServerContainerFactoryBean" class="org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean">
<property name="maxSessionIdleTimeout" value="30000"/>
</bean>
In this case, configuring SockJS in your app could go a long way:
<websocket:stomp-endpoint path="/cors/auth/clientEndpoint">
<websocket:handshake-handler ref="myHandshakeHandler" />
<websocket:sockjs/>
</websocket:stomp-endpoint>
This will give you:
better HTTP clients support
heartbeat management
If you want to actually close a session from STOMP endpoints, then I suggest you to vote/follow the SPR-12288 JIRA issue.
To access websocket session you can use the following approach:
https://stackoverflow.com/a/32270216/2982835
As a follow-up to this question, I'm still a bit confused about how to properly use the CXF-RS component.
I'm confused why we need the <cxf:rsServer> tag for specifying CXF-RS endpoints (or is there even such a concept?), when I can use the <jaxrs:server> tag perfectly fine.
Here's my configuration XML for both Camel and CXF:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:camel="http://camel.apache.org/schema/spring"
xsi:schemaLocation="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
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<jaxrs:server id="userService" address="/users">
<jaxrs:serviceBeans>
<bean class="com.example.UserServiceNoop" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
<bean id="user" class="org.apache.camel.component.direct.DirectComponent" />
<camel:camelContext id="someCamelContext">
<camel:route id="userServiceRoute">
<camel:from uri="cxfrs:bean:userService" />
<camel:routingSlip>
<camel:simple>user:${header.operationName}</camel:simple>
</camel:routingSlip>
</camel:route>
<camel:route id="userServiceRetrieveUser">
<from uri="user:retrieveUser" />
<!-- Assume this is going to a useful Processor -->
</camel:route>
</camel:camelContext>
</beans>
UserService.java:
package com.example;
/* a bunch of imports... */
public interface UserService {
#GET
#Path(value="/{user.id}")
#Produces({MediaType.APPLICATION_JSON})
public User retrieveUser(
#PathParam("user.id") Integer id
);
}
UserServiceNoop.java
package com.example;
/* a bunch of imports ... */
public class UserServiceNoop implements UserService
{
#Override
public User retrieveUser(Integer id) {
throw new RuntimeException();
}
}
In this example, I'm not using any <cxf:rsServer> tag, yet it works fine. I know it goes through the CXF-RS component, because when I run the application, it doesn't throw any RuntimeExceptions, which is the expected behavior when using CXF-RS (the method implementation in the service class will not be called).
Am I missing something by not using this tag?
As the other answer says, the cxf:rsServer is mainly used to be processed by a Camel route as in the jaxrs:server the processing of the request is done by a classic controller.
For example:
Classic JAXRS server:
You will declare a classic Bean Rest (Controller) and inject a Service inside.
Sample of an XML config (extract):
<jaxrs:server id="deviceServiceSvcV1" address="/device/v1">
<jaxrs:serviceBeans>
<ref component-id="deviceServiceRest" />
</jaxrs:serviceBeans>
<!-- and other providers, interceptors, etc... here -->
</jaxrs:server>
<!-- Service bean -->
<bean id="deviceServiceRest" class="org.mycomp.device.rest.v1.ws.api.DeviceServiceRest">
<property name="deviceService" ref="deviceService" />
</bean>
The Controller class will process the request / response in a classic way (e.g. calling an injected service).
Camel route with cxf:rsServer
Sample of an XML config (extract):
<cxf:rsServer id="rsServer" address="/device/v1"
serviceClass="org.mycomp.device.rest.v1.ws.api.DeviceServiceRest">
<cxf:properties>
<!-- whatever here -->
</cxf:properties>
<!-- and other interceptors, etc... here -->
</cxf:rsServer>
and in the classes:
#Produces({ MediaType.APPLICATION_XML })
#Path("/")
public class DeviceServiceRest {
#GET
public Response listDevicess(
#QueryParam("model") String model,
#QueryParam("sid") String sid,
) {
return null; // never used
}
#GET
#Path("{id}")
public Response getDeviceById(
#PathParam("id") String id,
#QueryParam("model") String model,
#QueryParam("sid") String sid
){
return null; // never used
}
}
The REST Controller has empty methods (returning null) but I think the latest camel-cxf supports now an Interface which is more elegant than having methods returning null.
Now, the request processing can be implemented by a Camel Route like this:
from("cxfrs:bean:rsServer?synchronous=true")
.routeId("cxf-device-rest-v1")
.process( new CheckAuthenticationProcessor())
.choice()
.when(header("operationName").isEqualTo("listDevice"))
.setHeader("backenOperation").constant("list")
.setHeader("backendResource").constant("device")
.endChoice()
.when(header("operationName").isEqualTo("getDeviceById"))
.setHeader("backenOperation").constant("retrieve")
.setHeader("backendResource").constant("device")
.endChoice()
.end()
.bean("requestProcessor")
.to(InOut, backendEndpoint)
.process(checkResponseStatusCode())
.bean(new HttpResponseProcessor())
;
And you can also control the request / response processing as you want from the route.
These are two different kind of implementing a REST API (server side) but in my opinion this is a bit old school as modern framework like spring-boot does not need any of these.
I found the second way a bit too much overkill as I like Camel for integration purpose but using it for a REST API could be subject to discussion.
One use-case I can see is a HTTP REST Web-Service for asynchronous processing, the service responding 202 Accepted and the Camel Route making an integration of the request in asynchronous mode especially when a specific Camel Component can be easily used instead of a complex class (or any need of the EIP patterns).
You use cxf:reserver tag when you want to use CXF endpoint as the consumer of something. Say for example in a complex Apache Camel route or in Spring integration. is used when you are the provider of the endpoint serving requests.
I'm using spring and in my client, a web app, I need to interact with a Jax-WS webservice. I currently have it working via annotating the service interface with the #WebServiceRef annotation. However, I need the wsdlLocation property to be injected because, obviously Sun Microsystems or Oracle, the web service wsdl location in production will be different to what's being used during development.
How can I inject the wsdlLocation?
Here's an extremely simplified version of the code:
//This client service lives in the web app. wsimport used to generate artifacts.
#Component
public class MyClientServiceImpl implements MyClientService {
#WebServiceRef(wsdlLocation = "http://localhost:8080/ws/MyOtherService/the.wsdl", value = MyOtherServiceService.class)
//Interface generated by wsimport
private MyOtherService otherService;
#Override
public List<SomeSearchData> search(String searchString) {
return otherService.search(searchString);
}
}
This is semi outlined in the JAX-WS FAQ. You need to inject the endpoint string as a standard member variable and then use...
((BindingProvider)proxy).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, this.injectedEnpointURL);
You can use LocalJaxWsPortProxyFactoryBean. You can configure WSDL URL (among other things) via this factory bean. Here is a configuration snippet from the official documentation:
<bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="example.AccountService"/>
<property name="wsdlDocumentUrl" value="http://localhost:8888/AccountServiceEndpoint?WSDL"/>
<property name="namespaceUri" value="http://example/"/>
<property name="serviceName" value="AccountService"/>
<property name="portName" value="AccountServiceEndpointPort"/>
</bean>
Then you can let Spring autowire this dependency into your target bean (e.g. MyClientServiceImpl).