Integration of JAX-WS to Spring Boot - java

I have an existing API which has both JAX-RS and JAX-WS. I want to migrate it into a Spring Boot application. What I've done for JAX-RS part is registering that class:
#GET
#Path("/ping")
#Produces("text/plain")
String ping();
into a Jersey Config which extends ResourceConfig. Here is the example from JAX-WS of same class:
#WebMethod(operationName = "Ping", action = "ping-app")
String ping();
Since I've used reference implementations of JAX-RS and JAX-WS I hope that it should be easy to migrate it into Spring Boot. I've easily done JAX-RS integration. Is there any such simple way to integrate JAX-WS too?

Ideally you'd want to use a Spring Boot Starter to assist you. According to their documentation:
Starters are a set of convenient dependency descriptors that you can
include in your application. You get a one-stop-shop for all the
Spring and related technology that you need, without having to hunt
through sample code and copy paste loads of dependency descriptors.
For example, if you want to get started using Spring and JPA for
database access, just include the spring-boot-starter-data-jpa
dependency in your project, and you are good to go.
Using this list of official and community starters, it looks like you have the following options:
spring-boot-starter-jersey should be able to handle JAX-RS
spring-boot-web-services might be able to handle JAX-WS. From what I understand, you might need to do things the 'Spring Web Services' way which is different to JAX-WS. It's been a while since I worked with Spring Web Services, so I might be incorrect on this point.
The Apache CXF starter can support both JAX-RS and JAX-WS which seems to meet your requirement. They have this guide for their spring boot integration for you to look at.

I didn't use this personally but it seems that spring provides starter pack for JAX-WS
Take a look at this artifact. It should be enough just to add this dependency to your project:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>

I have solved the problem by using this dependency
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.3.6</version>
</dependency>
and add this bean in the configuration bean
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
#Configuration
public class WebServiceConfig{
#Autowired
private Bus bus;
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(bus, new SpPortImpl());
endpoint.publish("/NpcdbService");
return endpoint;
}
}
SpPortImpl is the class annotated with #WebService

Related

Spring Webflux returns 404 ( Not Foud )

I have to save some values in a reactive way using spring Webflux. But when I send the request then 404 status is returned as a response.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
EmpController class
#RestController
#RequestMapping("/emp")
public class EmpController {
private EmployeeRepository empRepo;
#Autowired
public EmpController(EmployeeRepository empRepo)
{
this.empRepo=empRepo;
}
#PostMapping("/save")
#Consumes({MediaType.APPLICATION_JSON})
public void saveEmp(#RequestBody Mono<Employee> emp)
{
emp.subscribe(e-> {
e.setDate(new Date());
empRepo.saveEmp(e);
});
}
}
When I send the request via PostMan then 404(not found) is returned.
JAX-RS is a specification within Java EE of how to code REST api's. Several libraries have then implemented said specification, Like Jersey or restEasy. WHen building a Java EE application, you needed one of these libraries to be able to build a rest api.
Spring built their own way of building rest apis spring-web for non reactive applications, and spring-webflux for reactive applications.
Jersey and restEasy (to my knowledge) only works if you are building a non-reactive application.
In order to make your code work you need to remove:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
Jersey is a Java EE implementation of JAX-RS. It is used in Java EE to build Java EE styled rest apis. You are building a reactive application, using Spring Webflux which has its own way of building REST api's.
Spring is not a Java EE application. When you added that dependency, spring assumed that you wanted to build a Spring Application but not use the Spring built in REST api functions and annotations etc, so it didn't register your code, that you have written with Springs way of building rest apis.
It assumed you were going to write a REST api the "jersey way" and if you are using jersey you need to register your api classes manually. And (to my knowledge) Jersey only works with non-webflux applications.
This is all mainly basic knowledge, and if you dont understand why i suggest you read up and build a regular spring boot application, before trying out webflux.
I suggest you read the following parts:
Reactive programming, Reactor getting started
Baeldung Webflux
Building a reactive Webflux Application
Spring boot Webflux
It is Strange but when I removed jersey dependency and it Worked. Still not sure about the reason behind it.
raised pull request you the merge the same to take the changes I have done
https://github.com/Benzeman97/reactive-async-app/pull/1
You need to remove below Jersey dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
The reason behind is that the spring-boot-starter-jersey is a starter for building Restful web applications using JAX-RS and Jersey. Since you have used it in your project the spring does not use in built spring functions for rest api's like #GetMapping, #PostMapping.
If you want to use jersey to create the rest api then use #GET annotation for Get api and the #Produces to defined the mapping as below.
Eg.
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.springframework.stereotype.Service;
#Service
#Path("/hello")
public class HelloService {
#GET
#Produces("text/plain")
public String hello() {
return "Hello from Spring";
}
}
Also you have to register this class in JerseyConfig.
import com.zetcode.endpoint.HelloService;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(HelloService.class);
}
}
And if you want to go ahead with the spring build in functions and use reactive just remove the jersey dependency and use webflux dependency to create your rest api's.
I had the same problem.
The solution:
go to application.properties,
remove server.servlet.context-path
and add spring.webflux.base-path

Don't spring-boot-starter-web and spring-boot-starter-webflux work together?

When I start to learn the spring-webflux, I have the question about this component.
I built a simple project, using maven to manage it. I addded the dependencies related to spring-boot-starter-web and spring-boot-starter-webflux, like :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
But it doesn't work. When removing the spring-boot-starter-web dependency, it can work well.
As explained in the Spring Boot reference documentation section about the web environment, adding both web and webflux starters will configure a Spring MVC web application.
This is behaving like that, because many existing Spring Boot web applications (using MVC) will depend on the webflux starter to use the WebClient. Spring MVC partially support reactive return types, so this is an expected use case. The opposite isn't really true, since a reactive application is not really likely to use Spring MVC bits.
So using both web and webflux starters is supported, but it will configure a Spring MVC application. You can always force the Spring Boot application to be reactive with:
SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)
But it's still a good idea to clean up dependencies as it would be easy to use a blocking feature in your reactive web application.
I had a similar issue using spring-boot-starter-webflux and spring-data-geode causing
DEBUG [http-nio-8082-exec-2] org.sprin.web.servl.resou.ResourceHttpRequestHandler 454 handleRequest: Resource not found
It was resolved by changing the application type
#SpringBootApplication
public class Web {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Web.class);
app.setWebApplicationType(WebApplicationType.REACTIVE);
SpringApplication.run(Web.class, args);
}
}
The whole class looks like this
After setting the application type, if I don't then call the SpringApplication in a static way, I get this:

Jersey with Spring 4 Dependency Injection

I am to use Dependency Injection of Spring framework version 4. I have seen that the Jersey has its DI with plugin
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.12</version>
</dependency>
Is Jersey's DI of Spring recommended or is there special reason to use it? What if Spring 4 DI is used independently ?
Also please let me know any step by step learning source to integration Spring DI with Jersey ?
The jersey-spring3 extension is not a stand-alone Dependency Injection feature, it's just an extension which makes Jersey aware of Spring's managed beans.
From Jersey - Spring DI:
Jersey provides an extension to support Spring DI. This enables Jersey to use Spring beans as JAX-RS components (e.g. resources and providers) and also allows Spring to inject into Jersey managed components.
...
The above module does not add any transitive dependency to Spring modules, so you will need to add Spring 3 dependencies explicitly into your dependency list.
So if you want to use Jersey with Spring you need jersey-spring3 and all the Spring dependencies you normally use.
By the way, the jersey-spring3 extension is compiled against Spring 3, but should work with Spring 4. See Using Jersey-spring with Spring 4.0 for reference.
You should add jersey-spring3.jar first like the document in jersey website.
For this step by step learning source to integration Spring DI with Jersey, you can do like this when you start up you application debug the application.
Find ServletContainer.class and set a breakpoint in init() function, as this you can find this step by step.

Spring Boot vs. Apache CXF for RESTful Web Services?

I am part of a coding competition, the task is to create a RESTful online marketplace where users can post buy and sell requests via http.
I need to build a front end web service that accepts and stores these requests.
The tech requirements include both Spring boot and CXF. As far as I am aware, both CXF and Spring boot are capable of accepting http requests.
In spring boot, you use a controller like:
#Controller
#EnableAutoConfiguration
public class controller {
#RequestMapping("/")
#ResponseBody
String home() {
return "Hello, World!";
}
}
Whereas with CXF (using javax.ws.rs), the code might look like this:
#WebService(serviceName = "MarketService", targetNamespace = "http://localhost:9005")
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public interface MarketService {
#GET
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces({ MediaType.APPLICATION_JSON })
#Path("/sells/{id}")
public prod getProduct(#PathParam("id") int id);
Can someone help me understand the fundamental difference between these two approaches to handling http requests? Is there a way I can use both Spring Boot and CXF in the same application?
Spring MVC and Apache CXF are 2 separate frameworks to handle HTTP requests and that can be used to build REST web services.
Spring MVC is a project under the Spring "umbrella" (and therefore strongly tied to the Spring framework on top of which it's built),
Apache CXF is a open-source implementation of JAX-RS (REST) and JAX-WS (SOAP). Apache CXF can be run standalone or be included in a Spring application.
If you are looking to build a REST web service, they are pretty much mutually exclusive (you have to pick one). If all you're going to do is build REST web services, then they're pretty much equivalent. If you also need to have an MVC framework to serve HTML pages, then Spring MVC has that capability (CXF does not).
Personal opinion: Spring MVC is easier to get started with (thanks to Spring Boot which handles most of the configuration for you) than CXF (which requires more XML configuration).
PS: in your CXF example, you have a #WebService annotation. This annotation is part of JAX-WS (SOAP), not JAX-RS (REST). You probably don't need it.
Use the Spring Boot CXF JAX-RS starter by adding:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
<version>3.1.7</version>
</dependency>
See also: http://cxf.apache.org/docs/springboot.html
Check this project out for nice starter for JAX-RS (REST) that leverages CXF on Tomcat via TomEE.
Everything is all setup and ready to go.
https://github.com/tomitribe/tomee-jaxrs-starter-project
Long description here:
http://www.tomitribe.com/blog/2014/06/apache-tomee-jax-rs-and-arquillian-starter-project
Note, running CXF "Standalone" still requires a Servlet container (Tomcat or Jetty), so the above is several steps completed, simplified and finished in a small starter project. Designed for impatient people (like myself) that don't like to read directions and just like to start hacking. Always easier for me to start with something that works and then tweak it.

How to set up JAX-RS Application using annotations only (no web.xml)?

Is it possible to set up a JAX-RS application using annotations only? (using Servlet 3.0 and JAX-RS Jersey 1.1.0)
I tried and had no luck. Using some web.xml seems required.
Configuration A (working, but has web.xml configuration)
web.xml
...
<servlet>
<servlet-name>org.foo.rest.MyApplication</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>org.foo.rest.MyApplication</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
...
Java
#ApplicationPath("/")
public class MyApplication extends Application {
...
}
Configuration B (not working, exception thrown)
#ApplicationPath("/")
#WebServlet("/*") // <--
public class MyApplication extends Application {
...
}
The latter seems to insist that the Application will be a subclass of Servlet (the exception leaves no guesswork)
java.lang.ClassCastException: org.foo.rest.MyApplication cannot be cast to javax.servlet.Servlet
Questions
Why the web.xml definition worked but the annotation didn't? What's the difference?
Is there a way to have it worked, e.g. have a JAX-RS Application with no web.xml?
** PLEASE READ IF YOU USE TOMCAT OR JETTY! **
The accepted answer does work, but only if the webapp is deployed to an app server like Glassfish or Wildfly, and possibly servlet containers with EE extensions like TomEE. It doesn't work on standard servlet containers like Tomcat, which I'm sure most people looking for a solution here want to use.
If you're using a standard Tomcat install (or some other servlet container), you need to include a REST implementation since Tomcat doesn't come with one. If you're using Maven, add this to the dependencies section:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>jaxrs-ri</artifactId>
<version>2.13</version>
</dependency>
...
</dependencies>
Then just add an application config class to your project. If you don't have any special configuration needs aside from setting the context path for the rest services, the class can be empty. Once this class is added, you don't need to configure anything in web.xml (or have one at all):
package com.domain.mypackage;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("rest") // set the path to REST web services
public class ApplicationConfig extends Application {}
After this, declaring your web services is straight forward using the standard JAX-RS annotations in your Java classes:
package com.domain.mypackage;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.Path;
// It's good practice to include a version number in the path so you can have
// multiple versions deployed at once. That way consumers don't need to upgrade
// right away if things are working for them.
#Path("calc/1.0")
public class CalculatorV1_0 {
#GET
#Consumes("text/plain")
#Produces("text/plain")
#Path("addTwoNumbers")
public String add(#MatrixParam("firstNumber") int n1, #MatrixParam("secondNumber") int n2) {
return String.valueOf(n1 + n2);
}
}
This should be all you need. If your Tomcat install is running locally on port 8080 and you deploy your WAR file to the context myContext, going to...
http://localhost:8080/myContext/rest/calc/1.0/addTwoNumbers;firstNumber=2;secondNumber=3
...should produce the expected result (5).
It seems that all I needed to do is this (Servlet 3.0 and above)
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/*")
public class MyApplication extends Application {
...
}
And no web.xml configuration was apparently needed (tried on Tomcat 7)
Chapter 2 of the JAX-RS: Java™ API for RESTful Web Services specification describes the publication process of a JAX-RS application in Servlet environment (section 2.3.2 Servlet in the specification).
Please note that Servlet 3 environment is recommended only (section 2.3.2 Servlet, page 6):
It is RECOMMENDED that implementations support the Servlet 3
framework pluggability mechanism to enable portability between
containers and to avail themselves of container-supplied class
scanning facilities.
In short, if you want to use a no-web.xml approach, it's possible with a custom implementation of javax.ws.rs.core.Application that registers RESTful service resources with the javax.ws.rs.ApplicationPath annotation.
#ApplicationPath("/rest")
Although you asked specifically about Jersey you may also like to read the article Implementing RESTful services with JAX-RS and WebSphere 8.5 Liberty Profile in which I described the no-web.xml publication process for WebSphere Liberty Profile (with Apache Wink as the implementation of JAX-RS).
You need to setup the right dependencies in pom.xml
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
More details here:
Starter example for jax-rs
The previously mentioned dependencies did not work for me. From the Jersey User guide:
Jersey provides two Servlet modules. The first module is the Jersey core Servlet module that provides the core Servlet integration support and is required in any Servlet 2.5 or higher container:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
</dependency>
To support additional Servlet 3.x deployment modes and asynchronous JAX-RS resource programming model, an additional Jersey module is required:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
The jersey-container-servlet module depends on jersey-container-servlet-core module, therefore when it is used, it is not necessary to explicitly declare the jersey-container-servlet-core dependency.
https://jersey.github.io/documentation/latest/deployment.html#deployment.servlet.3
As #Eran-Medan pointed out, JBoss EAP 7.1 (note without a Web Application so no servlet, I was doing it in a EJB 3.2 project) I had to add the "value" attribute as such as I was getting an exception that the value attribute was required.
This worked for me
#ApplicationPath(value="/*")
public class MyApplication extends Application {
private Set singletons = new HashSet();
public MyApplication() {
singletons.add(new MyService());
}
...
}
Stack Trace
Caused by: java.lang.annotation.IncompleteAnnotationException: javax.ws.rs.ApplicationPath missing element value
at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:80)
at com.sun.proxy.$Proxy141.value(Unknown Source)
... 21 more

Categories

Resources