Spring Webflux returns 404 ( Not Foud ) - java

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

Related

Use Java Websocket API in Spring Boot application

i want to use the java websocket API in a Spring Boot application.
I created the following class as described here:
https://www.baeldung.com/java-websockets
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
#ServerEndpoint(value = "/test")
public class FrontendEndpoint {
#OnOpen
public void onOpen(Session session) throws IOException {
session.getBasicRemote().sendText("Test");
}
#OnMessage
public void onMessage(Session session, String message) throws IOException {
}
#OnClose
public void onClose(Session session) throws IOException {
}
#OnError
public void onError(Session session, Throwable throwable) {
}
}
I try to connect to that websocket but nothing happens. I saw a lot of articles in the internet but nothing helped me. I dont know how to get it to work.
When i try to connect to the websocket nothing happend.
Spring Boot version: 2.0.3
Websocket-API version: 1.1
I also don't see an open port for the websocket.
Thanks
BR
I once created a sample application with Spring Boot and Websocket API
https://github.com/simasch/spring-boot-websocket
but the problem is that the ServerEndpoint is not managed by Spring but by the Websocket implementation. So this is maybe not the way you should use websockets with Spring Boot.
I would recommend to have a look how Spring think that Websockets should be used:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket
It's a bit late, but in case someone will come here for a solution.
It is not covered in Spring documentation how to use Java WebSocket API (JSR-356) in Spring and I didn't manage to find any tutorial describing how to achieve it despite I've spent several hours googling it.
In order to use Java WebSocket API with Spring you should use class org.springframework.web.socket.server.standard.SpringConfigurator:
https://docs.spring.io/spring/docs/current/javadoc-api/index.html?org/springframework/web/socket/server/standard/SpringConfigurator.html
from
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>...</version>
</dependency>
Just add it to your
#ServerEndpoint(... configurator = SpringConfigurator.class)
and it is done. Your annotated class becomes a usual Spring-managed component legal for #Autowired injection and so on.
Hope it helps someone :)
UPD.
It should be mentioned, that JSR 356 WebSocket initialization through the SpringConfigurator is only supported in .war projects, that are to be deployed as usual artifacts in servlet container.
It will not work as expected when project is packaged in executable Spring Boot .jar or Spring Boot executable .war and when it is deployed it standalone mode via java -jar {app}
I didn't manage to find a way to initialize JSR 356 WebSocket with #Autowired Spring components in it in Spring Boot environment in executable archive.
It is possible to deploy JSR 356 WebSocket via ServerEndpointExporter.setAnnotatedEndpointClasses(...), but every class provided must have a no-arg constructor otherwise exception will be thrown. #Autowired field will not be processed by Spring in this case.
If you want to use java websocket API in a Spring Boot application you have to add this to spring configuration
#Configuration
public class WebSocketConfig {
#Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
Here is a full article.

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:

Integration of JAX-WS to Spring Boot

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

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