I am learning Akka with Java. I have written a simple program with two actors.
My first actor ActorA is called with list containing 1000 strings. ActorA loops through the list and calls ActorB for each element.
ActorB makes a Http POST call to external service using the String parameter received from ActorA.
I am expecting that ActorB will successfully make 1000 Http POST calls and will receive equal number of responses. However ActorB is able to make POST request randomly between 80-120 times then it stops making POST calls.
I tried providing a custom dispatcher as HTTP POST call is a blocking operation but still no luck!!
Refer to code and configuration given below.
public class ActorA extends AbstractActor {
static public Props props() {
return Props.create(ActorA.class);
}
static public class IdWrapper {
List<String> ids;
public IdWrapper(List<String> ids) {
this.ids = ids;
}
}
#Override
public Receive createReceive() {
return receiveBuilder()
.match(IdWrapper.class, this::process)
.build();
}
private void process(IdWrapper msg) {
msg.ids.forEach(id -> {
context().actorSelection("actorB").tell(new MessageForB(id), ActorRef.noSender());
}
);
}
}
public class ActorB extends AbstractActor {
final Http http = Http.get(getContext().system());
final Materializer materializer = ActorMaterializer.create(context());
public static Props props() {
return Props.create(ActorB.class);
}
static public class MessageForB implements Serializable {
String id;
public MessageForB(String id) {
this.id = id;
}
}
#Override
public Receive createReceive() {
return receiveBuilder()
.match(MessageForB.class, this::process)
.build();
}
private void process(MessageForB messageForB) {
ExecutionContext ec = getContext().getSystem().dispatchers().lookup("my-blocking-dispatcher");
/**
* Get id from request
*/
String reqId = messageForB.id;
/**
* Prepare request
*/
XmlRequest requestEntity = getRequest(Stream.of(reqId).collect(Collectors.toList()));
String requestAsString = null;
try {
/**
* Create and configure JAXBMarshaller.
*/
JAXBContext jaxbContext = JAXBContext.newInstance(XmlRequest.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
/**
* Convert request entity to string before making POST request.
*/
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(requestEntity, sw);
requestAsString = sw.toString();
} catch (JAXBException e) {
e.printStackTrace();
}
/**
* Create RequestEntity from request string.
*/
RequestEntity entity = HttpEntities.create(
MediaTypes.APPLICATION_XML.toContentType(HttpCharsets.ISO_8859_1),
requestAsString);
/**
* Create Http POST with necessary headers and call
*/
final CompletionStage<HttpResponse> responseFuture =
http.singleRequest(HttpRequest.POST("http://{hostname}:{port}/path")
.withEntity(entity));
responseFuture
.thenCompose(httpResponse -> {
/**
* Convert response into String
**/
final CompletionStage<String> res = Unmarshaller.entityToString().unmarshal
(httpResponse.entity(), ec, materializer);
/**
* Consume response bytes
**/
httpResponse.entity().getDataBytes().runWith(Sink.ignore(), materializer);
return res;
})
.thenAccept(s -> {
try {
/**
* Deserialize string to DTO.
*/
MyResponse MyResponse = getMyResponse(s);
// further processing..
} catch (JAXBException e) {
e.printStackTrace();
}
});
}
private XmlRequest getRequest(List<String> identifiers){
XmlRequest request = new XmlRequest();
// Business logic to create req entity
return request;
}
private MyResponse getMyResponse(String s) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance
(MyResponse.class);
javax.xml.bind.Unmarshaller jaxbUnmarshaller = jaxbContext
.createUnmarshaller();
StringReader reader = new StringReader(s);
return (MyResponse)
jaxbUnmarshaller.unmarshal(reader);
}
}
my-blocking-dispatcher {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
core-pool-size-min = 5
core-pool-size-max = 20
}
throughput = 1
}
Where can I improve or correct my code so that ActorB will successfully be able to make Http POST calls for all the items sent by ActorA ?
As i see you have used http.singleReques.
According to the akka-http docs
For these cases Akka HTTP offers the Http().singleRequest(...) method, which simply turns an HttpRequest instance into Future[HttpResponse]. Internally the request is dispatched across the (cached) host connection pool for the request’s effective URI.
http.singleRequest uses connection pool to handle requests so you need to increase number of connections in connection pool from akka http config.
in host-connection-pool section with this defaults:
host-connection-pool {
max-connections = 4
min-connections = 0
max-retries = 5
max-open-requests = 32
pipelining-limit = 1
idle-timeout = 30 s
}
Solution 2 :
using http.outgoingConnection
according to the akka-http docs it will be create an specific connection per request. So you can handle 1000 connections in parallel without connection pool.
With the connection-level API you open a new HTTP connection to a target endpoint by materializing a Flow returned by the Http().outgoingConnection(...) method. Here is an example:
def run(req:String): Unit ={
val apiBaseUrl = "example.com" //without protocol
val path = "/api/update"
val body = HttpEntity(ContentTypes.`application/json`,req.getBytes)
val request = HttpRequest(HttpMethods.POST, path,entity = body)
val connectionFlow = Http().outgoingConnection(apiBaseUrl)
val result = Source.single(request).via(connectionFlow).runWith(Sink.head)
result.onComplete{
case Success(value) =>
println(value)
case Failure(e)=>
e.printStackTrace()
}
}
Related
I am developing prototype for a new project. The idea is to provide a Reactive Spring Boot microservice to bulk index documents in Elasticsearch. Elasticsearch provides a High Level Rest Client which provides an Async method to bulk process indexing requests. Async delivers callbacks using listeners are mentioned here. The callbacks receive index responses (per requests) in batches. I am trying to send this response back to the client as Flux. I have come up with something based on this blog post.
Controller
#RestController
public class AppController {
#SuppressWarnings("unchecked")
#RequestMapping(value = "/test3", method = RequestMethod.GET)
public Flux<String> index3() {
ElasticAdapter es = new ElasticAdapter();
JSONObject json = new JSONObject();
json.put("TestDoc", "Stack123");
Flux<String> fluxResponse = es.bulkIndex(json);
return fluxResponse;
}
ElasticAdapter
#Component
class ElasticAdapter {
String indexName = "test2";
private final RestHighLevelClient client;
private final ObjectMapper mapper;
private int processed = 1;
Flux<String> bulkIndex(JSONObject doc) {
return bulkIndexDoc(doc)
.doOnError(e -> System.out.print("Unable to index {}" + doc+ e));
}
private Flux<String> bulkIndexDoc(JSONObject doc) {
return Flux.create(sink -> {
try {
doBulkIndex(doc, bulkListenerToSink(sink));
} catch (JsonProcessingException e) {
sink.error(e);
}
});
}
private void doBulkIndex(JSONObject doc, BulkProcessor.Listener listener) throws JsonProcessingException {
System.out.println("Going to submit index request");
BiConsumer<BulkRequest, ActionListener<BulkResponse>> bulkConsumer =
(request, bulkListener) ->
client.bulkAsync(request, RequestOptions.DEFAULT, bulkListener);
BulkProcessor.Builder builder =
BulkProcessor.builder(bulkConsumer, listener);
builder.setBulkActions(10);
BulkProcessor bulkProcessor = builder.build();
// Submitting 5,000 index requests ( repeating same JSON)
for (int i = 0; i < 5000; i++) {
IndexRequest indexRequest = new IndexRequest(indexName, "person", i+1+"");
String json = doc.toJSONString();
indexRequest.source(json, XContentType.JSON);
bulkProcessor.add(indexRequest);
}
System.out.println("Submitted all docs
}
private BulkProcessor.Listener bulkListenerToSink(FluxSink<String> sink) {
return new BulkProcessor.Listener() {
#Override
public void beforeBulk(long executionId, BulkRequest request) {
}
#SuppressWarnings("unchecked")
#Override
public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
for (BulkItemResponse bulkItemResponse : response) {
JSONObject json = new JSONObject();
json.put("id", bulkItemResponse.getResponse().getId());
json.put("status", bulkItemResponse.getResponse().getResult
sink.next(json.toJSONString());
processed++;
}
if(processed >= 5000) {
sink.complete();
}
}
#Override
public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
failure.printStackTrace();
sink.error(failure);
}
};
}
public ElasticAdapter() {
// Logic to initialize Elasticsearch Rest Client
}
}
I used FluxSink to create the Flux of Responses to send back to the Client. At this point, I have no idea whether this correct or not.
My expectation is that the calling client should receive the responses in batches of 10 ( because bulk processor processess it in batches of 10 - builder.setBulkActions(10); ). I tried to consume the endpoint using Spring Webflix Client. But unable to work it out. This is what I tried
WebClient
public class FluxClient {
public static void main(String[] args) {
WebClient client = WebClient.create("http://localhost:8080");
Flux<String> responseFlux = client.get()
.uri("/test3")
.retrieve()
.bodyToFlux(String.class);
responseFlux.subscribe(System.out::println);
}
}
Nothing is printing on console as I expected. I tried to use System.out.println(responseFlux.blockFirst());. It prints all the responses as a single batch at the end and not in batches at .
If my approach is correct, what is the correct way to consume it? For the solution in my mind, this client will reside is another Webapp.
Notes: My understanding of Reactor API is limited. The version of elasticsearch used is 6.8.
So made the following changes to your code.
In ElasticAdapter,
public Flux<Object> bulkIndex(JSONObject doc) {
return bulkIndexDoc(doc)
.subscribeOn(Schedulers.elastic(), true)
.doOnError(e -> System.out.print("Unable to index {}" + doc+ e));
}
Invoked subscribeOn(Scheduler, requestOnSeparateThread) on the Flux, Got to know about it from, https://github.com/spring-projects/spring-framework/issues/21507
In FluxClient,
Flux<String> responseFlux = client.get()
.uri("/test3")
.headers(httpHeaders -> {
httpHeaders.set("Accept", "text/event-stream");
})
.retrieve()
.bodyToFlux(String.class);
responseFlux.delayElements(Duration.ofSeconds(1)).subscribe(System.out::println);
Added "Accept" header as "text/event-stream" and delayed Flux elements.
With the above changes, was able to get the response in real time from the server.
In my app I am using netflix zuul to route a request from a microservice (gateway) to another. The requests are being routed fine but I also want to introduce some parameters in the request body before it is routed to the appropriate microservice. For this I am using Zuul pre filter like this.
public class SimpleFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(SimpleFilter.class);
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
try {
RequestContext context = RequestContext.getCurrentContext();
InputStream in = (InputStream) context.get("requestEntity");
if (in == null) {
in = context.getRequest().getInputStream();
}
String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
// body = "request body modified via set('requestEntity'): "+ body;
body = body.toUpperCase();
context.set("requestEntity", new ByteArrayInputStream(body.getBytes("UTF-8")));
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return null;
}
}
For now I am just trying to change the body to upper case but the microservice to which this request is routed doesn't receive the modified body (upper case). Instead it receives the original one. Am I doing something wrong. Any help would be appreciated. Thanks !!
Was able to do the following - transform a GET request to a POST request, and add body content to the (proxied) POST request.
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
context.addZuulRequestHeader("Content-Type", "application/x-www-form-urlencoded");
String body = String.format("a=%s&b=%s", a, b);
final byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
context.setRequest(new HttpServletRequestWrapper(context.getRequest()) {
#Override
public ServletInputStream getInputStream() {
return new ServletInputStreamWrapper(bytes);
}
#Override
public int getContentLength() {
return bytes.length;
}
#Override
public long getContentLengthLong() {
return bytes.length;
}
#Override
public String getMethod() {
return "POST";
}
});
return null;
}
try this one It's may be work in your case .
requestContext.getCurrentContext().put("requestEntity", new ByteArrayInputStream(body.getBytes("UTF-8")));
Turned out this method cannot change the request body within the requestContext. Truly in the requestContext, a new field "requestEntity" is added, however, the request body from context.getRequest().getInputStream() remains the same after this operation.
You can modify the request body, see this answer for an example. You just need to wrap the new request data and make sure you correctly report it's new content length.
I have an implementation of Hystrix Circuit Breaker and when testing I'm getting a Hystrix Runtime Exception with the error that The CircuitBreker timed-out and fallback failed. Do I need to increase a timeout on the CircutBreaker? Should it just trip the circuit breaker if the code times out?
My junit test is as follows:
#Test
public void test4(){
client = new DefaultHttpClient();
httpget = new HttpGet("http://www.google.com:81");
resp = new CircuitBreaker(client, "test4", httpget).execute();
//assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, resp.getStatusLine().getStatusCode());
System.out.println(resp.getStatusLine().getStatusCode());
}
My Class is just to run web gets/puts/etc using the CircuitBreaker in case of failure somehow. My class is as follows:
public class CircuitBreaker extends HystrixCommand<HttpResponse> {
private HttpClient client;
private HttpRequestBase req;
protected String key;
//Set up logger
private static final Logger logger = (Logger)LoggerFactory.getLogger(CircuitBreaker.class);
/*
* This method is a constructor and sets http client based on provides args.
* This version accepts user input Hystrix key.
*/
public CircuitBreaker (HttpClient client, String key, HttpRequestBase req, int threshold) {
super(HystrixCommandGroupKey.Factory.asKey(key));
this.client = client;
this.key = key;
this.req = req;
logger.info("Hystrix Circut Breaker with Hystrix key:" + key);
logger.setLevel(Level.DEBUG);
HystrixCommandProperties.Setter().withCircuitBreakerEnabled(true);
HystrixCommandProperties.Setter().withCircuitBreakerErrorThresholdPercentage(threshold);
//HystrixCommandProperties.Setter().withCircuitBreakerRequestVolumeThreshold(50);
}
/*
* This method is a constructor and sets http client based on provides args.
* This version uses the default threshold of 50% failures if one isn't provided.
*/
public CircuitBreaker (HttpClient client,String key, HttpRequestBase req){
this(client, key, req, 50);
}
/*
* This method runs the command and returns the response.
*/
#Override
protected HttpResponse run() throws Exception {
HttpResponse resp = null;
resp = client.execute(req);
if (resp != null)
logger.info("Request to " + req.getURI() + " succeeded!");
return resp;
}
/*
* Fallback method in in the event the circuit breaker is tripped.
* Overriding the default fallback implemented by Hystrix that just throws an exception.
* #see com.netflix.hystrix.HystrixCommand#getFallback()
*/
#Override
protected HttpResponse getFallback() {
//For later expansion as needed.
logger.error("Circuit Breaker has " + getExecutionEvents() + ". Reason: "+ getFailedExecutionException().getMessage());
return null;
}
}
You can try to increase the timeout on your CircuitBreaker and see what happens:
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000)
Because according to the Hystrix Wiki, the default timeout of HystrixCommand is 1 second, and it might take more than 1 second for your HttpGet return something.
You shouldn't need to increase the timeout to make a simple get request to google. Try this.
public class HttpCommand extends HystrixCommand<HttpResponse> {
private final HttpClient client;
private final HttpRequestBase req;
public HttpCommand(HttpClient client, HttpRequestBase req) {
super(HystrixCommandGroupKey.Factory.asKey("HttpCommandGroup"));
this.client = client;
this.req = req;
}
#Override
protected HttpResponse run() throws Exception {
return client.execute(req);
}
}
And a simple test
#Test
public void executeCommandTest(){
HttpClient client = HttpClientBuilder.create().build();
HttpGet httpget = new HttpGet("http://www.google.com");
HttpResponse resp = new HttpCommand(client, httpget).execute();
assertEquals(HttpStatus.SC_OK, resp.getStatusLine().getStatusCode());
}
I'm trying to obtain a list of a user's tweets and I've run into some trouble when trying to authenticate my call to the API. I currently get a 401 when executing the code below:
public interface TwitterApi {
String API_URL = "https://api.twitter.com/1.1";
String CONSUMER_KEY = "<CONSUMER KEY GOES HERE>";
String CONSUMER_SECRET = "<CONSUMER SECRET GOES HERE>";
String ACCESS_TOKEN = "<ACCESS TOKEN GOES HERE>";
String ACCESS_TOKEN_SECRET = "<ACCESS TOKEN SECRET GOES HERE>";
#GET("/statuses/user_timeline.json")
List<Tweet> fetchUserTimeline(
#Query("count") final int count,
#Query("screen_name") final String screenName);
}
The following throws a 401 Authorisation error when calling fetchUserTimeline()
RetrofitHttpOAuthConsumer consumer = new RetrofitHttpOAuthConsumer(TwitterApi.CONSUMER_KEY, TwitterApi.CONSUMER_SECRET);
consumer.setTokenWithSecret(TwitterApi.ACCESS_TOKEN, TwitterApi.ACCESS_TOKEN_SECRET);
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(TwitterApi.API_URL)
.setClient(new SigningOkClient(consumer))
.build();
TwitterApi twitterApi = restAdapter.create(TwitterApi.class)
tweets = twitterApi.fetchUserTimeline(2, screenName);
I've also included the relevant code from the signpost-retrofit plugin:
public class SigningOkClient extends OkClient {
private final RetrofitHttpOAuthConsumer mOAuthConsumer;
public SigningOkClient(RetrofitHttpOAuthConsumer consumer) {
mOAuthConsumer = consumer;
}
public SigningOkClient(OkHttpClient client, RetrofitHttpOAuthConsumer consumer) {
super(client);
mOAuthConsumer = consumer;
}
#Override
public Response execute(Request request) throws IOException {
Request requestToSend = request;
try {
HttpRequestAdapter signedAdapter = (HttpRequestAdapter) mOAuthConsumer.sign(request);
requestToSend = (Request) signedAdapter.unwrap();
} catch (OAuthMessageSignerException | OAuthExpectationFailedException | OAuthCommunicationException e) {
// Fail to sign, ignore
e.printStackTrace();
}
return super.execute(requestToSend);
}
}
The signpost-retrofit plugin can be found here: https://github.com/pakerfeldt/signpost-retrofit
public class RetrofitHttpOAuthConsumer extends AbstractOAuthConsumer {
private static final long serialVersionUID = 1L;
public RetrofitHttpOAuthConsumer(String consumerKey, String consumerSecret) {
super(consumerKey, consumerSecret);
}
#Override
protected HttpRequest wrap(Object request) {
if (!(request instanceof retrofit.client.Request)) {
throw new IllegalArgumentException("This consumer expects requests of type " + retrofit.client.Request.class.getCanonicalName());
}
return new HttpRequestAdapter((Request) request);
}
}
Any help here would be great. The solution doesn't have to include the use of signpost but I do want to use Retrofit. I also do not want to show the user an 'Authenticate with Twitter' screen in a WebView - I simply want to display a handful of relevant tweets as part of a detail view.
Are you certain the signpost-retrofit project works for twitter oauth? I've used twitter4j successfully in the past - and if you don't want the full library you can use their code for reference. twitter4j
i have implemented rest webservices using Jersey, and whenever some exception occur on the server side, the client gets a generic HTTP 500 Internal Server Error, with no more info of the real exception. I found that people usually catch any exception on the server side, then throws a WebApplicationException, but even this way the client keeps getting the generic HTTP 500 Internal Server Error.
This is my webservice:
#PUT
#Produces(MediaType.APPLICATION_XML)
#Consumes(MediaType.APPLICATION_XML)
#Path("/transmitir")
public WrapperTransmissaoRetorno receber(WrapperTransmissao wrapperRecepcao) {
WrapperTransmissaoRetorno retorno = new WrapperTransmissaoRetorno();
retorno.setCodigoMaster(new Random().nextInt());
retorno.setDataRetorno(new Date());
if(true){
throw new WebApplicationException("Este pau eh bem graudo");
}
return retorno;
}
This is the code that calls the client:
try {
WsTransmissaoCliente client = new WsTransmissaoCliente();
WrapperTransmissao wrapperRecepcao = new WrapperTransmissao();
Transferencia transferencia = new Transferencia();
transferencia.setCodigoTabela(23);
transferencia.setCodigoTransferencia(56);
transferencia.setDataRetorno(new Date());
transferencia.setDataTransmissao(new Date(System.currentTimeMillis()+3000000));
transferencia.setNomeTabela("CUPOM");
transferencia.setTipoOperacao(TipoOperacao.UPDATE);
wrapperRecepcao.setTransferencia(transferencia);
Jumento jumento = new Jumento();
jumento.setIdade(24);
jumento.setNome("José");
wrapperRecepcao.setObjeto(jumento);
// Cabrito cabrito = new Cabrito();
// cabrito.setAltura(56);
// cabrito.setPeso(120.0);
// wrapperRecepcao.setObjeto(cabrito);
WrapperTransmissaoRetorno retorno = client.transmitir(wrapperRecepcao);
System.out.println("Retorno do WS: "+retorno);
} catch (Exception e) {
WebApplicationException exx = (WebApplicationException) e;
exx.printStackTrace();
}
How to avoid this and get the real exception? Or at least the message?
UPDATE
Here is the object i am sending as a response:
package br.atualy.integracaocheckout.wrappers;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class WrapperTransmissaoRetorno {
private Date dataRetorno;
private Integer codigoMaster;
public Date getDataRetorno() {
return dataRetorno;
}
public void setDataRetorno(Date dataRetorno) {
this.dataRetorno = dataRetorno;
}
public Integer getCodigoMaster() {
return codigoMaster;
}
public void setCodigoMaster(Integer codigoMaster) {
this.codigoMaster = codigoMaster;
}
#Override
public String toString() {
return "WrapperRecepcaoRetorno{" + "dataRetorno=" + dataRetorno + ", codigoMaster=" + codigoMaster + '}';
}
}
UPDATE 2
And here is the client:
import br.atualy.integracaocheckout.wrappers.WrapperTransmissao;
import br.atualy.integracaocheckout.wrappers.WrapperTransmissaoRetorno;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.WebTarget;
public class WsTransmissaoCliente {
private final WebTarget webTarget;
private final Client client;
private static final String BASE_URI = "http://localhost:8080/IntegracaoCheckout/webresources";
public WsTransmissaoCliente() {
client = javax.ws.rs.client.ClientBuilder.newClient();
webTarget = client.target(BASE_URI).path("transmissao");
}
// public String receber() throws ClientErrorException {
// WebTarget resource = webTarget;
// resource = resource.path("receber");
// return resource.request(javax.ws.rs.core.MediaType.APPLICATION_XML).get(String.class);
// }
public WrapperTransmissaoRetorno transmitir(WrapperTransmissao requestEntity) throws ClientErrorException {
return webTarget.path("transmitir")
.request(javax.ws.rs.core.MediaType.APPLICATION_XML)
.put(javax.ws.rs.client.Entity.entity(requestEntity, javax.ws.rs.core.MediaType.APPLICATION_XML), WrapperTransmissaoRetorno.class);
}
public void close() {
client.close();
}
}
If using jawax.ws.rs.core.Response object.
SERVER :: In case of exception/failure set it as :
// do stuff
// here e.getMessage() can be custom failure message too
response = Response.serverError().entity(e.getMessage()).build();
// return response object
return response;
CLIENT :: On the client side check following :
if(response != null && reponse.getStatus() == Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) {
String serverErrorMsg = response.readEntity(String.class);
throw new Exception(serverErrorMsg);
}
Generally it's better to declare your method as returning a Response object instead of a user-defined type, and set the data as the entity. Then if you want to indicate that an exception has happened, you can just pass that exception as the entity of the Response you are returning.
e.g.
#GET
#Path("/foo")
public Response getFoo() {
try {
// do stuff
return Response.ok(someData).build();
} catch (Exception e) {
return Response.serverError().entity(e).build();
}
}
You'll notice that this way you don't ever end up actually throwing an exception out of your method, but rather return an explicit 500 response with an exception as the entity. This way you can still throw exceptions out of your code, but they'll be handled nicely.
EDIT
I'm not sure what your client wrapper code is doing, but you can pass the expected response data type into your call with the normal REST client:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://foo.com/foo");
String response = target.request().get(String.class);
or you can also pull it out of the Response using the readEntity() method:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://foo.com/foo");
Response response = target.request().get();
String entity = response.readEntity(String.class);
It sounds like what you need to do is check the return code, and then parse the entity as a either a WrapperTransmissaoRetorno or a WebApplicationException depending on what code was returned:
Response response = client.transmitir(wrapperRecepcao);
if (response.getStatus() == Response.Status.OK.getStatusCode()) { // 200
WrapperTransmissaoRetorno retorno = response.readEntity(WrapperTransmissaoRetorno.class);
// do stuff
} else if (response.getStatus() == Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) { // 500
WebApplicationException e = response.readEntity(WebApplicationException.class);
// do stuff
} // etc for other response codes
Use response object in webapplication excemption. It should work.
From java docs:
WebApplicationException(String message)
Construct a new instance with a blank message and default HTTP status code of 500.
Its a blank message. I haven't tried it myself. I guess this is the problem.
https://jersey.java.net/apidocs/2.6/jersey/javax/ws/rs/WebApplicationException.html
Even after all the suggestions i could not manage to throw the exception to the client.
So what i did was to put a String property inside my returning class, so when an exception occurs on the server side, this String will contain the exception message and i can get it on the client.