Creating SOAP client using Quarkus cannot import #CXFClient - soap

Working with SOAP Web service in Quarkus using quarkiverse/quarkus-cxf
Try to implement a client using this example
import com.example.FruitWebService; // SEI
import javax.enterprise.context.ApplicationScoped;
import io.quarkiverse.cxf.annotation.CXFClient;
#Application // or any other CDI scope
public class MySoapClient {
#Inject #CXFClient
FruitWebService clientService;
public int getCount() {
return this.clientService.count();
}
}
Cannot import #CXFClient Im using the following quarkus-cxf extension
<dependency>
<groupId>io.quarkiverse.cxf</groupId>
<artifactId>quarkus-cxf</artifactId>
<version>0.7</version>
</dependency>

You need at least Version 0.8 of the plugin. So update your pom.xml to:
<dependency>
<groupId>io.quarkiverse.cxf</groupId>
<artifactId>quarkus-cxf</artifactId>
<version>0.8</version>
</dependency>

Related

Quarkus could not find writer for content-type multipart/form-data rest client

I'm implementing an API-gateway for my rest microservice using Quarkus.
I want to forward requests to another (Quarkus) rest-api.
I'm trying to forward a POST request with multiform data.
I'm expecting to get a 201 but I'm getting a 500 internal server error.
RESTEASY throws the following error:
RESTEASY002020: Unhandled asynchronous exception, sending back 500: javax.ws.rs.ProcessingException: RESTEASY004655: Unable to invoke request: javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type multipart/form-data type: org.acme.rest.client.multipart.MultipartBody
I've tried upgrading my Quarkus version from 1.4.2 to 1.5.2 because I saw the following issue:
https://github.com/quarkusio/quarkus/issues/8223
Also tried Intellij invalidate cache/restart, re-import maven
Code
MultiPartBody:
package org.acme.rest.client.multipart;
import org.jboss.resteasy.annotations.providers.multipart.PartType;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.MediaType;
public class MultipartBody {
#FormParam("sample_id")
#PartType(MediaType.TEXT_PLAIN)
public Long sampleId;
#FormParam("username")
#PartType(MediaType.TEXT_PLAIN)
public String username;
#FormParam("content")
#PartType(MediaType.TEXT_PLAIN)
public String content;
}
Interface:
package org.acme.rest.client;
import io.smallrye.mutiny.Uni;
import org.acme.rest.client.multipart.MultipartBody;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#RegisterRestClient
#Consumes(MediaType.MULTIPART_FORM_DATA)
public interface SocialService {
#POST
Uni<Response> create(#MultipartForm MultipartBody data);
}
Resource:
package org.acme.rest.client;
import io.smallrye.mutiny.Uni;
import org.acme.rest.client.multipart.MultipartBody;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/comments")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public class SocialResource {
#Inject
#RestClient
SocialService socialService;
#POST
public Uni<Response> create(#MultipartForm MultipartBody data) {
return socialService.create(data);
}
}
Test:
package org.acme.rest.client;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
#QuarkusTest
public class SocialResourceTest {
#Test
public void create(){
given().contentType("multipart/form-data")
.multiPart("sample_id", "1")
.multiPart("username", "testuser")
.multiPart("content", "test message")
.when()
.post("/comments")
.then()
.statusCode(201);
}
}
To fix this issue with Resteasy and quarkus, I had to add the MultipartFormDataWriter to resteasy client registry, this is my new code :
Any question click connect to my twitter #AIDeveloper
MultipartFormDataOutput output = new MultipartFormDataOutput();
output.addFormData("file", fileInputStream, MediaType.APPLICATION_OCTET_STREAM_TYPE);
output.addFormData("status", "status1", MediaType.TEXT_PLAIN_TYPE);
Response uploadResponse = newClient()
.target(uploadUri)
.register(MultipartFormDataWriter.class)
.request()
.post(Entity.entity(output
, MediaType.MULTIPART_FORM_DATA_TYPE));
Everything looks fine, but maybe you are missing this dependency
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
</dependency>
That info is available at this quarkus guide
I've had the same issue (could not find writer for content-type multipart/form-data). I solved it by making the MultiPartBody extend MultipartFormDataOutput.
In your case this would be:
...
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
...
public class MultipartBody extends MultipartFormDataOutput {
...
}
I found this solution by looking at how Quarkus / Resteasy internally resolves the output writers. This is done in the resolveMessageBodyWriter() method of org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl. The relevant writer is org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataWriter. There, the isWriteable() method checks if MultipartFormDataOutput is a superclass of your class (MultipartBody).
However, I don't know why it works without extending MultipartFormDataOutput in the Quarkus examples.
My Quarkus version is 1.8.2.Final and I use the io.quarkus.quarkus-resteasy-multipart maven dependency, not org.jboss.resteasy.resteasy-multipart-provider.
The ProcessingException with message RESTEASY003215: could not find writer for content indicates no serializer was bound for the request type. This usually means you're missing the rest client dependency needed for the type.
In your case that might be today (as of Quarkus 2.5.0)
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-multipart</artifactId>
</dependency>
Another possibility for this very same error is you're trying to implement a REST client with the reactive API and missing the reactive client API bindings. You can have both reactive and regular rest-client at the same time in your project.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-reactive-jackson</artifactId>
</dependency>
It might seems unrelated because it brings the Json writers in that case, but the exception and error message you'll get is exactly the same.

How I should configure a POJO class to enable it to be used as a resource in JAX-RS rest service? (Liferay 7.1)

I can use String object in JAX-RS rest service but not able to use POJO object. How I should configure a POJO class to enable it to be used as a resource in JAX-RS rest service?
DTO class
public class RestServiceDTO {
private String groupId;
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId)
this.groupId = groupId;
}
#Override
public String toString() {
return "RestServiceDTO [groupId=" + groupId + "]";
}
}
Rest service:
#Component(
immediate = true,
property = {
JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + "=/greetings",
},
service = Application.class
)
public class RestServiceApplication extends Application {
public Set<Object> getSingletons() {
return Collections.<Object>singleton(this);
}
#POST
#Path("/post")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String test(RestServiceDTO dto) {
String groupid = dto.getGroupId();
return "{'groupid':'" + groupid + "'}";
}
}
Error:
2019-02-12 13:33:58.021 ERROR [http-nio-8080-exec-1][JAXRSUtils:83] No
message body reader has been found for class com.dto.RestServiceDTO,
ContentType: application/json
Since version 7.1 Liferay supports the OSGi JAX-RS Whiteboard specification, which means support for JAX-RS 2.1 using CXF, which also mean that there is support for JAXB annotated POJOs.
If you need to return a simple POJO you would normally be OK just by annotating your POJO class with #XmlRootElement.
Make sure you get JAXB runtime support attached to your application by requiring it on your application component configuration putting the property osgi.jaxrs.extension.select=(osgi.jaxrs.name=jaxb-json) on your application component.
This property will instruct the JAX-RS whiteboard to not start your application until the extension is present and ready for your application.
Since Liferay 7.2 the JAXB default implementation has been changed to Jackson. There is no configuration change needed, but now every POJO can be serialized to JSON even if the POJO is not annotated. Just make sure the jaxb runtime support is attached to your application the same as above.
In both 7.1 and 7.2 you can check the status of your JAX-RS applications, and attached extensions, using the gogo shell command jaxrs:check
Liferay uses the Apache CXF implementation of JAX-RS. As #dkb mentioned in the comments you need to provide the annotation that you have in the sample code.
You need to add the dependencies. See the list below and note that some are provided by the platform but some needs to be included in your jar and don't forget the transitive dependencies.
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.7.9</version>
</dependency>
The last thing is. You need to register your Jackson provider within the JAX-RS app. It is done in teh applicaiton class for example like this (there are more ways how to do it).
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(getJacksonProvider());
return Collections.unmodifiableSet(singletons);
}
Add dependency
for MAVEN:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.9.10</version>
</dependency>
for GRADLE:
compileOnly group: 'com.fasterxml.jackson.jaxrs', name: 'jackson-jaxrs-json-provider', version: '2.9.10'
now add this method in your Apllication class
private JacksonJsonProvider getJacksonJsonProvider() {
JacksonJsonProvider jacksonJsonProvider = new JacksonJsonProvider();
ObjectMapper objectMapper = new ObjectMapper();
// Prevent serialization of null and empty string values
objectMapper.setSerializationInclusion(Include.NON_EMPTY);
jacksonJsonProvider.setMapper(objectMapper);
jacksonJsonProvider.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return jacksonJsonProvider;
}
now change the code of getSingletons() method of your Application class to
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(this);
singletons.add(getJacksonJsonProvider());
return Collections.unmodifiableSet(singletons);
}
now I think you have to change your return statement to
return Response.ok(JSONFactoryUtil.looseSerializeDeep("{'groupid':'" + groupid + "'}"), MediaType.APPLICATION_JSON).build();
but I am not sure that you have to change your return statement or yours one will run ok

Which files has to be added in plugins to open the hadoop mapreduce source code in eclipse

Suppose I have written a follwing code in eclipse
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class WordCount {
public static class TokenizerMapper
extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context
) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
public static class IntSumReducer
extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values,
Context context
) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
Now, suppose I want to open the source code of Job/Configuration object(as obvious by clicking on it with ctrl pressed),eclipse throws error saying source code not found with Attach source tab.
I tried downloading few plugins but it din't work.
Please help,Thanks in advance.
(Note: I use eclipse mars,version 2.7.2 and OS is ubuntu 16.04 LTS)
The best way to set up Map-Reduce Projects in Eclipse is by using Maven.
First, setup a Maven Project in Eclipse.Follow this link : https://wiki.jasig.org/display/UPM32/Creating+a+Simple+Maven+Project
2.Next, update the pom.xml file of your maven project with the below tags. You can change the version tags for Hadoop related Artifacts according to your need (2.7.2 according to your question). Make sure JAVA_HOME is set in your system env variables.
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<scope>system</scope>
<version>1.8</version>//1.7 if you are using jdk 7
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.3</version>
</dependency>
</dependencies>
Right click on your eclipse project Go to Maven -> Click Update Project
and wait for a while. It will download all the dependent jars for executing a Hadoop Client.
4.Now, When you write your Mapreduce code, and try to view the source of any Class or Method(Job, in your case) Eclipse should not complain "source code not found with Attach source tab". You should be able to see the source code.

Can I generate a Spring Feign client with Multipart parameters?

I am getting the error:
"Method has too many Body parameters"
when trying to generate a Spring Feign client
#RequestMapping(value="/media", method=RequestMethod.POST)
String uploadMedia(#RequestHeader("Authentication") String token,
#RequestPart("media") MultipartFile audio,
#RequestPart("a-json-object") SomeClass someClazz,
#RequestPart("another-json-object") AnotherClass anotherClazz);
I found the following solution, which works when using regular Feign annotations, but not with Spring MVC annotations:
'Too many body parameters' Exception on Feign Client
It should be possible now. Add the following dependencies:
<dependencies>
...
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>2.2.0</version>
</dependency>
...
and use this client configuration:
#FeignClient(name = "file-upload-service", configuration = FileUploadServiceClient.MultipartSupportConfig.class)
public interface FileUploadServiceClient extends IFileUploadServiceClient {
#Configuration
public class MultipartSupportConfig {
#Bean
#Primary
#Scope("prototype")
public Encoder feignFormEncoder() {
return new SpringFormEncoder();
}
}
}
Example was taken from: feign-form docs

How to pass Calendar param as input to a rest service?

Here is what I am trying to do:
#POST
#Path("/MyPath")
#Produces("text/xml")
#RolesAllowed({"user"})
public Output myMethod (#QueryParam("itemId") long ItemId,
#QueryParam("plannedstartdate") Calendar plannedStartDate,
#QueryParam("plannedreturndate") Calendar plannedReturnDate)
I am using JBoss AS7. As far as I understan, resteasy is integrated into JBoss AS7. I am able to run simple rest services.
The only documentation I found about passing dates is at the link :
http://docs.jboss.org/resteasy/2.0.0.GA/userguide/html/StringConverter.html#StringParamUnmarshaller
I am not able to follow this and fix the issue as the instructions are not clear.
When I try to create an annotation DateFormat as given in the example, it does not recognize StringParamUnmarshaller. I don't know where to get it from. If resteasy is already integrated into JBoss AS7, is this not supposed to be recognized?
My pom.xml has the following dependency:
<!-- Import the JAX-RS API, we use provided scope as the API is included
in JBoss AS 7 -->
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_1.1_spec</artifactId>
<scope>provided</scope>
</dependency>
The calls to this method fail as the String to Calendar conversion does not happen. I dont want to pass String instead of Calendar as there are other clients that make java call directly. Can anyone help with how I can pass dates to Rest Calls?
Thanks
Veer
This issue is resolved. See the following code.
Create an Annotation class CalendarFormat.java:
#Retention(RUNTIME)
#StringParameterUnmarshallerBinder(CalendarFormatter.class)
public #interface CalendarFormat {
String value();
}
Add a class CalendarFormatter.java:
public class CalendarFormatter implements StringParameterUnmarshaller<Calendar> {
private SimpleDateFormat formatter;
public void setAnnotations(Annotation[] annotations) {
CalendarFormat format = FindAnnotation.findAnnotation(annotations, CalendarFormat.class);
formatter = new SimpleDateFormat(format.value());
}
public Calendar fromString(String str) {
try {
Calendar cal = Calendar.getInstance();
cal.setTime(formatter.parse(str));
return cal;
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
Add to POM
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.3.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>2.3.4.Final</version>
<exclusions>
<exclusion>
<artifactId>resteasy-jaxrs</artifactId>
<groupId>org.jboss.resteasy</groupId>
</exclusion>
</exclusions>
</dependency>
Change the method signature to use the annotation
#POST
#Path("/MyPath")
#Produces("text/xml")
#RolesAllowed({"user"})
public Output myMethod(#QueryParam("itemId") long ItemId,
#QueryParam("plannedstartdate") #CalendarFormat("MM-dd-yyyy") Calendar plannedStartDate,
#QueryParam("plannedreturndate") #CalendarFormat("MM-dd-yyyy") Calendar plannedReturnDate)
That's it.
It's not possible to use java.util.Calendar as an argument of #QueryParam, because it doesn't have a one-arg constructor that accepts String. The only option you have is to introduce a new class QueryCalendar, which will have one-arg constructor and will return Calendar (or inherit it).
More information about who can be an argument: http://docs.oracle.com/javaee/6/api/javax/ws/rs/QueryParam.html