I am trying to display the Service list served by a simple jax-ws service using CXF.
Using these dependencies:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.5.2</version>
</dependency>
And this class:
#WebService
public class Foo {
#WebMethod
public String helloWorld() {
return "hello";
}
}
I can publish the service with this line:
Endpoint.publish("http://0.0.0.0:1234/foo", new Foo());
Then I can display the wsdl browsing to the url: http://127.0.0.1:1234/foo?wsdl
But I don't find any way to obtain the service list html page. I tried the /services url ( from cxf By default, Apache CXF creates a /services page containing a listing of the available endpoints ), but nothing. Is there a way to enable it ?
Can it be done programmatically (not using xml file) ?
Related
I used Jersey and Webflux with R2DBC. after send the POST via the postman I got this message " Cannot construct instance of reactor.core.publisher.Mono "
This is my JerseyConfiguration:
#Component
public class JerseyConfiguration
extends ResourceConfig {
public JerseyConfiguration() {
register(ProductController.class, 1);
}
}
and this is my Controller:
#Path("/v1")
#Controller
public class ProductController {
#Autowired
private ProductService productService;
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("/product")
public Mono<Product> createProduct(#RequestBody Mono<Product> productMono){
return productMono.flatMap(this.productService::createProduct);
}
}
and this sis my service:
#Service
public class ProductService {
#Autowired
private ProductRepository repository;
public Mono<Product> createProduct(final Product product){
return this.repository.save(product);
}
}
and also this my pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Now, this is my problem; I got this message from the postman:
Cannot construct instance of `reactor.core.publisher.Mono` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 1]
Please let me know how to solve that problem.
Thank you
You cannot mix WebFlux and Jersey. You should choose one or the other, not both. They both provide an HTTP server engine, but:
Jersey is a Servlet JAX-RS implementation, it does not know anything about reactive streams, Mono, Flux, etc.
Webflux is the Spring HTTP server engine based on reactive streams and async Netty HTTP server.
If you look at Spring Boot reference documentation, section 3.5: Web, you will see that Jersey is one of the available engines, competing with other possible engines, i.e Web MVC and web reactive (webflux).
So, the answer is : Jersey is incompatible with Webflux, and you must choose between Webflux reactive Web and Spring rest annotation, or Jersey and Jax_RS without using Mono/Flux as return-type.
Note 1 : You should annotate your class with #RestController whe using webflux, so it understand that method return is the HTTP response body (see the last paragraph of reference documentation section 1.4.1: #Controller for details.
Note 2 : If you really want to use jersey, but you still require to consume Mono objects from other parts of your system, you might use one of the conversion functions provided by Reactor to return an object that jersey can work with. For example, on Mono object, you will find a toFuture() method. You could also block(), but it could be dangerous.
I'm trying to produce a bootable jar with Undertow + Resteasy + Jackson2 with those dependencies in my pom.xml:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-undertow</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-cdi</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
when I use 3.* versions of resteasy, I can start the WebServer this way:
public static UndertowJaxrsServer startServer() {
server = new UndertowJaxrsServer()
.deploy(MyOwnApplication.class) // replace this with .deployOldStyle(MyOwnApplication.class) for versions grater than 4.0 of resteasy
.start(
Undertow.builder()
.addHttpListener(Integer.parseInt(SERVER_PORT), SERVER_HOST)
);
return server;
}
but, after upgrading resteasy from v3.0.9.Final to v4.6.0.Final, this service does not work (always produces errors 405 - method not allowed, on every POST request).
The solution I found was to replace the deploy method with deployOldStyle (present only in versions grater than 4 of reasteasy), but it seems to be undocumented.
Can anybody explain me how the deploy method has changed and why?
Should I adapt some other part of my code and continue using the deploy method?
Thanks
I have been trying to set a a SOAP endpoint with Websocket as transport protocol via CXF and implement invoke it via CXF. With Embeded jetty. I have tried a couple of approaches non of the aproaches worked unfortunatly. Here is what I did:
Aproach 1. According to CXF documentation websocket is supported as transport protocol and its support is given via
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-websocket</artifactId>
<version>3.3.2</version>
</dependency>
I have setup the following dependencies:
<dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client</artifactId>
<version>2.0.39</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.3.2</version>
</dependency>
The code I executo is the following:
Endpoint endpoint = Endpoint.create(new MyHelloWorldServicePortType() {
#Override
public String sayHello(HelloMessage message) throws FaultMessage {
return message.sayHello();
}
};
((org.apache.cxf.jaxws.EndpointImpl)endpoint).getFeatures().add(new
WSAddressingFeature());
endpoint.publish("ws://localhost:8088/MyHelloWorldService" );
URL wsdlDocumentLocation = new URL("file:/path to wsdl file");
String servicePart = "MyHelloWorldService";
String namespaceURI = "mynamespaceuri";
QName serviceQN = new QName(namespaceURI, servicePart);
Service service = Service.create(wsdlDocumentLocation, serviceQN);
MyHelloWorldServicePortType port = service.getPort( MyHelloWorldServicePortType.class);
portType.sayHello(new HelloMessage("Say Hello"));
The result of this code is:
SEVERE: [ws] onError java.util.concurrent.TimeoutException: Request
timeout to not-connected after 60000 ms at
org.asynchttpclient.netty.timeout.TimeoutTimerTask.expire(TimeoutTimerTask.java:43)
at
org.asynchttpclient.netty.timeout.RequestTimeoutTimerTask.run(RequestTimeoutTimerTask.java:48)
at
io.netty.util.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:682)
at
io.netty.util.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:757)
at
io.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:485)
at java.base/java.lang.Thread.run(Thread.java:834)
jun. 12, 2019 1:13:33 P.M.
org.apache.cxf.transport.websocket.ahc.AhcWebSocketConduit$AhcWebSocketWrappedOutputStream
connect SEVERE: unable to connect
java.util.concurrent.ExecutionException:
java.util.concurrent.TimeoutException: Request timeout to
not-connected after 60000 ms at
java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395)
at
java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1999)
at
org.asynchttpclient.netty.NettyResponseFuture.get(NettyResponseFuture.java:172)
at
org.apache.cxf.transport.websocket.ahc.AhcWebSocketConduit$AhcWebSocketWrappedOutputStream.connect(AhcWebSocketConduit.java:309)
at
org.apache.cxf.transport.websocket.ahc.AhcWebSocketConduit$AhcWebSocketWrappedOutputStream.setupWrappedStream(AhcWebSocketConduit.java:167)
at
org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleHeadersTrustCaching(HTTPConduit.java:1343)
at
org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.onFirstWrite(HTTPConduit.java:1304)
at
org.apache.cxf.io.AbstractWrappedOutputStream.write(AbstractWrappedOutputStream.java:47)
at
org.apache.cxf.io.AbstractThresholdOutputStream.write(AbstractThresholdOutputStream.java:69)
at
org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1356)
at
org.apache.cxf.transport.websocket.ahc.AhcWebSocketConduit$AhcWebSocketWrappedOutputStream.close(AhcWebSocketConduit.java:139)
at
org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
I have absolutly no idea why. When I try to connect via websocket chrome client on the URL. It says success. At the same time when connecting via the client it says Timeout.
Aproach 2.
I decided to cheat CXF and provide a handmade Websocket endpoint that will be used as a front to the CXF webservice. The idea is that the Client will send a message via websocket the message will be unwrapped and then sent over CXF. This aproach is very similar to the aproach here but here it uses JMS as transport
https://github.com/pbielicki/soap-websocket-cxf
In oprder to do this I created the following Websocket enpoint:
#ServerEndpoint("/jaxWSFront")
public class JaxWSFrontEnd {
#OnOpen
public void onOpen(final Session session) {
System.out.println("Hellooo");
}
#OnMessage
public void onMessage(String mySoapMessage,final Session session) throws Exception{
// The goal here is to get the soap message and redirect it via SOAP web //service. The JaxWSFacade acts as a point that understands websocket and then //gets the soap content and sends it to enpoint that understands SOAP.
session.getBasicRemote().sendText("Helllo . Now you see me.");
System.out.println("Hellooo again");
}
#OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("Hellooo");
}
#OnError
public void onError(Throwable t, Session session) {
System.out.println("Hellooo");
}
}
Now I pointed my Client proxy to the jaxWsFrontEnd instead of the webservice endpoint. My expectation is that I will recieve the SOAP message in the onMessage method and then I will be able to forwards to SOAP to the CXF web service.
Now my code looks like this:
server = new Server(8088);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath( "/" );
server.setHandler(context);
ServerContainer container = WebSocketServerContainerInitializer.configureContext(context);
container.addEndpoint(JaxWSFrontEnd.class);
server.setHandler( context );
server.start();
Endpoint endpoint = Endpoint.create(new MyHelloWorldServicePortType() {
#Override
public String sayHello(HelloMessage message) throws FaultMessage {
return message.sayHello();
}
};
((org.apache.cxf.jaxws.EndpointImpl)endpoint).getFeatures().add(new
WSAddressingFeature());
URL wsdlDocumentLocation = new URL("file:/path to wsdl file");
String servicePart = "MyHelloWorldService";
String namespaceURI = "mynamespaceuri";
QName serviceQN = new QName(namespaceURI, servicePart);
Service service = Service.create(wsdlDocumentLocation, serviceQN);
MyHelloWorldServicePortType port = service.getPort( MyHelloWorldServicePortType.class);
portType.sayHello(new HelloMessage("Say Hello"));
For the second aproach I had in addition to the aproach 1 the following dependencies:
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-common</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
</dependency>
Result from aproach 2 is absolutly the same as Aproach 1 the exceptions I recieve are the same, with one minor difference. When I use the the Chrome websocket client and point it directly the the jaxWsFrontend I am able to successfuly send a message. Why I am not able to connect to websocket wia the CXF websocket transport mechanisms ???? What am I doing wrong ?
UPDATE: enabling the loging from NETTY. It apears that netty has thrown java.lang.NoSuchMethodError: io.netty.channel.DefaultChannelId.newInstance()Lio/netty/channel/DefaultChannelId;
Maybe I have a version compatability issue with netty. The version I can see is imported in the project is 4.1.33. It is a transitive dependency I don|t have it declared.
Ok I actualy managed to crack it alone. I will post the answer for completion. Apparantly CXF guys should update their documentation IMO. On their website it is stated that in order to enable Websocket as transport protocol we need
cxf-rt-transports-websocket dependency.
What they do not say is that you in addition need async-http-client not any version but 2.0.39 a prettey old one. The problem is that it automaticaly includes transitive dependencies to netty 4.1 and the error specified above begins to manifest. What you actualy need is nett 4.0.56
Here is the fragment that made the things work for me:
<dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client</artifactId>
<version>2.0.39</version>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.56.Final</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-websocket</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.3.2</version>
</dependency>
Aproach 1 is working
Aproach 2 I managed to trigger the onConnect event, the onMessage timedout, but in my opinion it should work I am missing something small. Anyway I don|t have more time to spent and I am happy with Aproach 1.
I'm trying to write a rest service to upload a file along with some other file information, using Jersey + Jackson.
Using multipart, the file is uploaded correctly, and simple fields are OK as well, but the POJO that's supposed to contain additional data, is always null.
Simplified example
POJO:
public class Test {
public String name;
public Test() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Application:
#ApplicationPath("myapp")
public class JerseyApp extends ResourceConfig {
public JerseyApp() {
register(MultiPartFeature.class);
register(JacksonFeature.class);
packages("com.test.rest");
// Enable Tracing support.
property(ServerProperties.TRACING, "ALL");
}
}
Service:
#Path("file")
public class FileRestService {
#POST
#Path("/upload1")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response createFile1(#FormDataParam("doc") Test doc) {
//doc is always null
return Response.ok(doc.getName()).build();
}
#POST
#Path("/upload2")
#Consumes(MediaType.APPLICATION_JSON)
public Response createFile2(Test doc) {
//doc is created ok
return Response.ok(doc.getName()).build();
}
web.xml is empty
pom.xml
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.22</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.22</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.22</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.22</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<version>1.11</version>
</dependency>
</dependencies>
Data is JSON and I'm testing with DHC/Postman, if it makes any difference.
Any idea why when using multipart, the pojo/bean is null?
See related problem here. The problem is that the Content-Type is not set for the doc part. In that post (answer) I didn't know how to set it in Postman, and I still haven't found a solution.
If you use a tool like cURL (which I'll just say is the best tool ever for REST development :-), you can make set the Content-Type of each part. If you don't know already cURL is a command like tool that you can use to make HTTP (and other protocol) requests. For example, you can do something like
curl -v -X POST http://localhost:8080/api/file \
-F 'doc={"hello":"world"};type=application/json'
This makes a POST request as multipart and sets the doc part to be of type application/json.
You will also find some useful examples of setting here
UPDATE
Another options, if you simply can't set the individual parts' Content-Type, is to set the type programmatically before deserialing. For example
#POST
#Path("/upload1")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response createFile1(#FormDataParam("doc") FormDataBodyPart part) {
part.setMediaType(MediaType.APPLICATION_JSON_TYPE);
Test doc = part.getValueAs(Test.class);
return Response.ok(doc.getName()).build();
}
I am following Jersey tutorial to develop simple Jersey web application.
By following Section - Example 2.9. Deployment of a JAX-RS application using #ApplicationPath with Servlet 3.0
I have created created below program:
#ApplicationPath("resources")
public class MyApplication extends PackagesResourceConfig {
public MyApplication() {
super("com.examples");
}
}
and I have below basic Resource class:
#Path("/helloworld")
public class HelloWorldResource {
#GET
#Produces("text/plain")
public String getClichedMessage() {
return "Hello World";
}
}
I am using Jersey-1.19 version, I am not having any web.xml file in my web application. Now I am deploying my application on Tomcat 7 server.
When I try to access the URL as : http://localhost:8080/myapp/resources/helloworld I am getting error as
HTTP Status 404 - /myapp/resources/helloworld
type Status report
message: /myapp/resources/helloworld
description: The requested resource is not available.
You need the jersey-servlet dependency
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.19</version>
</dependency>
if you want to go with no web.xml. It has the JerseyServletContainerInitializer required to load the application.
And just for any future readers that come across this looking for a Jersey 2.x solution, you need the following dependency to work with no web.xml
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey2.version}</version>
</dependency>
for the same reason - it has the JerseyServletContainerInitializer