I'm using a vendor-created SOAP client to access their SOAP service in my Jersey 1.3 REST application.
In certain cases, I would like like to access the response's XML, instead of the client's proxy class. Is there a way to do this?
I also have access to their WSDL if that would make this easier to do.
You can use JAX-WS Dispatch client to put your hands on XML:
import java.io.FileInputStream;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;
import org.w3c.dom.Node;
public class DispatchClient {
public static void main(String[] args) throws Exception {
String wsdlAddress = "http://127.0.0.1:8080/news/NewsWS?wsdl";
URL wsdl = new URL(wsdlAddress);
QName serviceName = new QName("http://news/", "NewsWebService");
QName portName = new QName("http://news/", "NewsPort");
//nie ma WSDL-a
Service service = Service.create(serviceName);
service.addPort(portName, SOAPBinding.SOAP12HTTP_BINDING, "http://127.0.0.1:8080/news/NewsWS");
Dispatch<SOAPMessage> dispatch = service.createDispatch(portName,
SOAPMessage.class, Service.Mode.MESSAGE);
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage req = mf.createMessage(null, new FileInputStream("NewsCountSoapRequest.xml"));
SOAPMessage res = dispatch.invoke(req);
SOAPBody body = res.getSOAPBody(); //SOAP body XML
}
}
You can work with SOAP body XML using DOM interface (all these Node madness) or use XPath.
Related
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.
I'm working on Spring Rest and in my Spring Rest app, if I try to produce json everything is OK. I can see it on browser. There is no error.
But if I want to produce XML, I use produces = "application/xml" or produces=MediaType.TEXT_XML_VALUE and
I getting this error:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Oct 23 18:30:51 EEST 2016
There was an unexpected error (type=Not Acceptable, status=406).
Could not find acceptable representation
My rest code is:
package getExample;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import pojo.Address;
import pojo.Person;
#RestController
public class GetExampleController {
#RequestMapping(value = "getExample",method=RequestMethod.GET,produces=MediaType.TEXT_XML_VALUE)
public List<Person> getExample1(#RequestParam(value = "personId", defaultValue = "0") String id) {
List<Person> personList = new ArrayList<>();
Person person1 = new Person("1", "ilkay", "günel",
new Address("Cennet Mah.", "K.Çekmece", "İstanbul", "TÜRKİYE"));
personList.add(person1);
Person person2 = new Person("2", "alican", "akkuş",
new Address("Cennet Mah.", "K.Çekmece", "İstanbul", "TÜRKİYE"));
personList.add(person2);
Person person3 = new Person("3", "mustafa", "demir",
new Address("Cennet Mah.", "K.Çekmece", "İstanbul", "TÜRKİYE"));
personList.add(person3);
if (id.equals("0")) {
return personList;
}
else {
return personList.subList(Integer.parseInt(id)-1, Integer.parseInt(id));
}
}
}
What is the error? Why can I get XML output? How can I solve this?
You need to add jackson-dataformat-xml's dependency:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Otherwise, you may annotate your bean with JAXB annotations.
Spring documentation:
If you have the Jackson XML extension (jackson-dataformat-xml) on the
classpath, it will be used to render XML responses and the very same
example as we used for JSON would work.
...
If Jackson’s XML extension is not available, JAXB (provided by default
in the JDK) will be used, with the additional requirement to have
[your class] annotated as #XmlRootElement...
...
To get the server to render XML instead of JSON you might have to send
an Accept: text/xml header (or use a browser).
I am new to REST and web services. I am trying to add two numbers with the below code.
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/calc")
public class CalcREST {
/*#GET
#Path("/add/{a}/{b}")
#Produces(MediaType.TEXT_PLAIN)
public String addPlainTextPost(#PathParam("a") double a, #PathParam("b") double b) {
return (a + b) + "";
}*/
#POST
#Path("/add/{a}/{b}")
#Produces(MediaType.TEXT_PLAIN)
public String addPlainTextPost(#PathParam("a") double a, #PathParam("b") double b) {
return addPlainText(a,b);
}
public String addPlainText(double a, double b) {
return (a + b) + "";
}
}
I am trying to test using both GET and POST. For both GET and POST I am invoking the APIs as
http://:9999/calcrest/calc/add/1/5
For Get I get the results properly. However If i comment out GET and keep POST, I am not able to get any results, just blank.
Any help would be appreciated.
I am assuming that you are calling this url from a browser and a browser is always going to do a GET on the url. If you want to try POST, you will have to either write a REST client in any one of your preferred languages (javascript, java) or if you are using firefox you can use POSTER plugin to simulate a POST. See here: POSTER.
Here is a quick REST client example (code snippet) in java. I have used jersey REST client.
WebResource webResource = restClient.resource("/calcrest/calc/add");
webResource = webResource.path("1").path("5");
String response = webResource.type(MediaType.TEXT_PLAIN)
.accept(MediaType.TEXT_PLAIN)
.post(ClientResponse.class);
System.out.println("Your addition: "+response);
I hope this helps!
I have implemented web service:
#WebServiceClient(//parameters//)
#HandlerChain(file = "handlers.xml")
public class MyWebServiceImpl {...}
Also I have implemented ObjectFactory with list of classes for creating my requests and responses. For Example class Test.
I need to get xml of response.
I try to use JAX-WS SOAP handler, so I add this #HandlerChain(file = "handlers.xml") anotation.
My handlers.xml looks like:
<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<handler-chain>
<handler>
<handler-class>java.com.webservice.service.LoggingHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>
My LoggingHandler class is:
import java.io.PrintWriter;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class LoggingHandler implements javax.xml.ws.handler.soap.SOAPHandler<SOAPMessageContext> {
public void close(MessageContext messagecontext) {
}
public Set<QName> getHeaders() {
return null;
}
public boolean handleFault(SOAPMessageContext messagecontext) {
return true;
}
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
System.out.println("\nOutbound message:");
} else {
System.out.println("\nInbound message:");
}
SOAPMessage message = smc.getMessage();
try {
PrintWriter writer = new PrintWriter("soap_responce" + System.currentTimeMillis(), "UTF-8");
writer.println(message);
writer.close();
message.writeTo(System.out);
System.out.println(""); // just to add a newline
} catch (Exception e) {
System.out.println("Exception in handler: " + e);
}
return outboundProperty;
}
}
I have test class which creates request, here are part of code:
MyWebServiceImpl impl = new MyWebServiceImpl(url, qName);
ws = impl.getMyWebServicePort();
Test req = new Test();
I suppose to get xml response in file "soap_responce" + System.currentTimeMillis(). But such file isn't even created. Please suggest how to get xml response, I'm new to web services and may do something wrong. Thanks
Using SOAP handlers is IMHO perfectly fine for such a task. I would approach it the same way.
I was able to use your configuration with minor modifications to get the example running. As a result I am able to see generated files. If you can't see them please check whether you check correct path, e.g. using:
File file = new File("soap_responce" + System.currentTimeMillis());
System.out.println(file.getAbsolutePath());
What I changed is:
package from java.com.webservice.service.LoggingHandler to com.webservice.service.LoggingHandler as packages starting with java are forbidden
Complete project can be found here:
https://github.com/destin/SO-answers/tree/master/SO-how-get-xml-responce-using-jax-ws-soap-handler
org.dpytel.jaxws.jaxws_java_first_jboss.client.Main class shows how I get and execute the web service.
BTW. you don't need to implement client stub and object factory etc when you have WSDL file. You can use wsimport tool. You can check how to use it in mentioned project.
In my CQ5.6 application,. as soon as the user hits a URL, I need to edit it using a certain parameters. All this must happen before Sling starts processing the URL.
I basically need to convert the URL like:
www.mysite.fr --> converts to --> /content/mysite/fr/
and so on....
I understand I'll need to create an OSGi bundle for this, but which API should I use to ensure that the URL is filtered by my class first and then catered by
Sling. ?
if you want a code-based solution for multiple websites (and you don't want to manage /etc/map) you can setup your own Filter:
package your.package;
import org.apache.felix.scr.annotations.*;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.service.component.ComponentContext;
#Component(immediate=true, enabled=true)
#Service(value=Filter.class)
#Properties({
#Property(name="sling.filter.scope", value="REQUEST", propertyPrivate=true),
#Property(name="service.ranking", intValue=-10000, propertyPrivate=true)
})
public class YourFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(ProductSEOFilter.class);
#Activate
protected void activate(ComponentContext ctx) throws Exception {
}
#Deactivate
protected void deactivate() throws Exception {
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws java.io.IOException, ServletException {
String lang = "en";
// 1. get domain and path
// 2. check if your conditions are met
// 3. extract language from domain
// 4. internal redirect
RequestDispatcher dispatch = request.getRequestDispatcher("/content/mysite/" + lang);
dispatch.forward(request, response);
}
public void destroy() {
}
}
you don't need to bother checking for and passing querystrings--those are carried on in the dispatcher. it only needs a new url to forward to.
You can do this via Sling URL Mapping without the need for a filter. The simpliest way to achieve this is to create a node under the /etc/map directory with a resource type of sling:Mapping & called www.mysite.fr.
This then takes a property of sling:internalRedirect — if an incoming request matches the node name, this property is appended to the path to continue with internal resource resolution.
<map>
<http jcr:primaryType="sling:OrderedFolder">
<www.mysite.fr
jcr:primaryType="sling:Mapping"
sling:internalRedirect="/content/mysite/fr"/>
</http>
</map>
The above will ensure any request coming to www.mysite.fr is resolved to www.mysite.fr/content/mysite/fr.
You can also pattern matching based on regex properties rather than names & include port numbers or schemes too. The full documentation is available on the Sling website.