We have the following Camel route in our application:
from(webServiceUri).routeId("webServiceRoute")
.unmarshal(jaxb)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
final Message in = exchange.getIn();
final DataRequest body = in.getBody(DataRequest.class);
final DataRequest.Items items = body.getItems();
itemValidator.validate(items.getItem());
getContext().createProducerTemplate().sendBody(importUri, body);
DataResponse response = new DataResponse();
response.setReturnCode(ReturnCode.SUCCESS);
in.setBody(response);
}
})
.marshal(jaxb);
We want the "webServiceRoute" to return the response user as soon as the processor has validated the data and forwarded the message to the "importUri". But right now it seems like the response is not returned to the caller until the "importUri" exchange is completed. So my question is what is the "correct" way to asynchronously forward the received request to another queue? There will not be any reply from the "importUri" exchange (i.e. it should be InOnly).
You can replace .sendBody(importUri, body) by .asyncSendBody(importUri, body).
Nevertheless I find your route looks strange to me, why do you use a processor to forward your message. I would write something like:
DataResponse successResponse = new DataResponse();
response.setReturnCode(ReturnCode.SUCCESS);
from(webServiceUri).routeId("webServiceRoute")
.unmarshal(jaxb)
.bean(WebServiceRouteHelper.class,"validate")
.to(importUri)
.setBody(constant(sucessResponse))
.marshal(jaxb);
class WebServiceRouteHelper {
public DataRequest validate(DataRequest dataRequest) throws Exception {
final DataRequest.Items items = body.getItems();
itemValidator.validate(items.getItem());
return dataRequest;
}
}
Related
I am working with spring boot with a h2 database. I would like to return a 201 message when the register is inserted succesfully and a 400 when is duplicated. I am using ResponseEntity to achieve this, fot example , the next is my create method from the Service:
#Override
public ResponseEntity<Object> createEvent(EventDTO eventDTO) {
if (eventRepository.findOne(eventDTO.getId()) != null) {
//THis is a test, I am looking for the correct message
return new ResponseEntity(HttpStatus.IM_USED);
}
Actor actor = actorService.createActor(eventDTO.getActor());
Repo repo = repoService.createRepo(eventDTO.getRepo());
Event event = new Event(eventDTO.getId(), eventDTO.getType(), actor, repo, createdAt(eventDTO));
eventRepository.save(event);
return new ResponseEntity(HttpStatus.CREATED);
}
This is my controller:
#PostMapping(value = "/events")
public ResponseEntity addEvent(#RequestBody EventDTO body) {
return eventService.createEvent(body);
}
But I'm not getting any message in the browser, I am doing different tests with postman and when I consult for all the events, the result is correct, but each time that I make a post I dont get any message in the browser, I am not pretty sure what is the cause of this issue. Any ideas?
The ideal way to send Response to the client is to create DTO/DAO with ResponseEntity in Controller
Controller.java
#PostMapping("/test")
public ResponseEntity<Object> testApi(#RequestBody User user)
{
System.out.println("User: "+user.toString());
return assetService.testApi(user);
}
Service.java
public ResponseEntity testApi(User user) {
if(user.getId()==1)
return new ResponseEntity("Created",HttpStatus.CREATED);
else
return new ResponseEntity("Used",HttpStatus.IM_USED);
// for BAD_REQUEST(400) return new ResponseEntity("Bad Request",HttpStatus.BAD_REQUEST);
}
Tested using Postman
Status 201 Created
Status 226 IM Used
Okay, I really don't feel good that service sending the ResponseEntity but not Controller.You could use #ResponseStatus and ExceptionHandler classes for these cases, like below.
Create a class in exception package
GlobalExceptionHandler.java
#ControllerAdvice
public class GlobalExceptionHandler {
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(DataIntegrityViolationException.class) // NOTE : You could create a custom exception class to handle duplications
public void handleConflict() {
}
}
Controller.java
#PostMapping(value = "/events")
#ResponseStatus(HttpStatus.CREATED) // You don't have to return any object this will take care of the status
public void addEvent(#RequestBody EventDTO body) {
eventService.createEvent(body);
}
Now changing the service would look like,
Service.java
#Override
public void createEvent(EventDTO eventDTO) { // No need to return
if (eventRepository.findOne(eventDTO.getId()) != null) {
throw new DataIntegrityViolationException("Already exists"); // you have to throw the same exception which you have marked in Handler class
}
Actor actor = actorService.createActor(eventDTO.getActor());
Repo repo = repoService.createRepo(eventDTO.getRepo());
Event event = new Event(eventDTO.getId(), eventDTO.getType(), actor, repo, createdAt(eventDTO));
eventRepository.save(event);
}
I want to send POST request with Retrofit + RxJava, but it is failing and I don't know the reason. In one activity it's working, in another - don't want to work:
private void sendMerchantInfo() {
try {
String advertiserOriginalDeepLink = "https://mywebsite.com/main-1?param1=value1¶m2=value2";
String urlGetParams = LinkParser.getUrlGETParams(advertiserOriginalDeepLink);
Map<Object, Object> merchantInfo = LinkParser.parseUrlGetParams(urlGetParams);
String merchantInfoJson = new Gson().toJson(merchantInfo); //{"param1":"value1","param2":"value2"}
String url = "https://api.endpoint.com/v1/system/merchant/process";
userService = this.serviceGenerator.createService(UserService.class, true);
final Observable observable = userService.sendUserInfo(
url, new RetrofitMapBody(merchantInfo))
.doOnNext(new Consumer<ResponseBody>() {
#Override
public void accept(ResponseBody responseBody) throws Exception {
//handle 200 OK.
}
})
.onErrorResumeNext((ObservableSource<? extends ResponseBody>) v ->
Crashlytics.log("Send user info attempt failed."))
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribe());
}
} catch (Exception exception) {
Crashlytics.log("Send user info attempt failed. " + exception.getMessage());
}
}
I suspect that problem in this part, I am trying to send request in OnCreate() method:
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
Tried to use this, but no effect:
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
What I am doing wrong? It always call onErrorResumeNext() It's probably something with threads because one time I got exception: networkonmainthreadexception. Please help.
Try using RxJava2 Adapter, it will save you a lot!
Step 1: Retrofit client setup
private Retrofit getRetrofitClient() {
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //option 1
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.newThread())) //option 2
.build();
}
Step 2: APIService interface (Example)
#GET("endpoint")
Single<ResponseModel> fetch();
Step 3: Usage
Single<ResponseModel> fetch() {
return getRetrofitClient()
.create(APIService.class)
.fetch();
}
Any non-2xx HTTP response will be wrapped in HttpException from which you can extract the status code, the status message and the full HTTP response.
Any connection errors will be wrapped in IOException
And that is all you need to do to wrap your network call in any RxJava stream.
I am building a simple REST api which connects a web server to a back end service, which performs a simple check and sends a response.
So client (over HTTP) -> to Web Server (over ACTIVEMQ/CAMEL)-> to Checking-Service, and back again.
The endpoint for the GET request is "/{id}". I'm trying to make this send a message through queue:ws-out to queue:cs-in and map it all the way back again to the original GET request.
The Checking-Service (cs) code is fine, it simply changes a value in the CheckMessage object to true using jmslistener.
I've searched the web thoroughly for examples, but can't get anything to work. The closest one I found was the following.
This is what I have so far on the Web Server (ws).
RestController
import ...
#RestController
public class RESTController extends Exception{
#Autowired
CamelContext camelContext;
#Autowired
JmsTemplate jmsTemplate;
#GetMapping("/{id}")
public String testCamel(#PathVariable String id) {
//Object used to send out
CheckMessage outMsg = new CheckMessage(id);
//Object used to receive response
CheckMessage inMsg = new CheckMessage(id);
//Sending the message out (working)
jmsTemplate.convertAndSend("ws-out", outMsg);
//Returning the response to the client (need correlation to the out message"
return jmsTemplate.receiveSelectedAndConvert("ws-in", ??);
}
}
Listener on ws
#Service
public class WSListener {
//For receiving the response from Checking-Service
#JmsListener(destination = "ws-in")
public void receiveMessage(CheckMessage response) {
}
}
Thanks!
your receive messages from "ws-in" with 2 consumers jmsTemplate.receiveSelectedAndConvert and WSListener !! message from a queue is consumed by one of the 2.
you send messages to "ws-out" and consume from "ws-in" ?? last queue
is empty and not receive any message, you have to send messages to
it
you need a valid selector to retrieve the message with receiveSelectedAndConvert based on JMSCorrelationID as the example you mntioned or the id received from the rest request but you need to add this id to the message headers like below
this.jmsTemplate.convertAndSend("ws-out", id, new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
TextMessage tm = session.createTextMessage(new CheckMessage(id));
tm.setJMSCorrelationID(id);
return tm;
}
});
return jmsTemplate.receiveSelectedAndConvert("ws-in", "JMSCorrelationID='" + id+ "'");
forward messages from "ws-out" to "ws-in"
#Service
public class WSListener {
//For receiving the response from Checking-Service
#JmsListener(destination = "ws-out")
public void receiveMessage(CheckMessage response) {
jmsTemplate.convertAndSend("ws-in", response);
}
}
How can I retrieve JMS Message headers after sending the message but without consuming the message ?
this is my message sending code
jmsTemplate.convertAndSend(que, text, message -> {
LOGGER.info("setting JMS Message header values");
message.setStringProperty(RequestContext.HEADER_ID, id);
// LOGGER.info(message.getJMSMessageId()); -- this gives a null value here
return message;
});
the message headers get generated only after the message is posted to the queue so iis there a simple way to retrieve JMS message headers when using MessagePostProcessor?
I've referred the links - here and here but not much of help :(
You can't get the JmsMessageID header until the message is actually sent; the post processor just allows you to modify the converted message JUST BEFORE it is sent.
However, your second link should work ok, since it saves off a reference to the message which you can access later.
EDIT
Confirmed:
#SpringBootApplication
public class So48001045Application {
public static void main(String[] args) {
SpringApplication.run(So48001045Application.class, args).close();
}
#Bean
public ApplicationRunner runner(JmsTemplate template) {
return args -> {
final AtomicReference<Message> msg = new AtomicReference<>();
template.convertAndSend("foo", "bar", m -> {
msg.set(m);
return m;
});
System.out.println(msg.get().getJMSMessageID());
};
}
}
and
ID:host.local-61612-1514496441253-4:1:1:1:1
I am using Jersey client for REST calls from Java code:
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.22.1</version>
</dependency>
In my GET request,
javax.ws.rs.client.Invocation.Builder builder = ClientBuilder.newClient().target(url).request();
builder.get().readEntity(String.class);
the client will be closed automatically after calling readEntity(String.class).
If I use,
builder.get(String.class);
I get the same output.
Is the connection closed automatically or do I need to close it manually in this case?
Short answer
Consider the following code:
Client client = ClientBuilder.newClient();
String result = client.target(url).request().get(String.class);
Under the hood, Jersey invokes Response#readEntity(Class<T>) if the request has succeeded and the connection will be closed for you. So the connection doesn't need to be closed manually in this situation.
Now consider the following code:
Client client = ClientBuilder.newClient();
Response response = client.target(url).request().get();
For this situation, you need to invoke Response#close() to close the connection. Or invoke Response#readEntity(Class<T>) to make Jersey close the connection for you.
Long answer
As stated in the documentation, if you don't read the entity, then you need to close the response manually by invoking Response#close().
For more details, have a look at Jersey's documentation about how to close connections:
5.7. Closing connections
The underlying connections are opened for each request and closed
after the response is received and entity is processed (entity is
read). See the following example:
final WebTarget target = ... some web target
Response response = target.path("resource").request().get();
System.out.println("Connection is still open.");
System.out.println("string response: " + response.readEntity(String.class));
System.out.println("Now the connection is closed.");
If you don't read the entity, then you need to close the response
manually by response.close().
Also if the entity is read into an
InputStream (by response.readEntity(InputStream.class)), the
connection stays open until you finish reading from the InputStream.
In that case, the InputStream or the Response should be closed
manually at the end of reading from InputStream.
Additionally, have a look at JerseyInvocation source. The most important parts are quoted below.
In the translate(ClientResponse, RequestScope, Class<T>) method you'll see that response.readEntity(Class<T>) is invoked.
JerseyInvocation.Builder#get(Class<T>)
Invoke HTTP GET method for the current request synchronously.
#Override
public <T> T get(final Class<T> responseType)
throws ProcessingException, WebApplicationException {
return method("GET", responseType);
}
JerseyInvocation.Builder#method(String, Class<T>)
Invoke an arbitrary method for the current request synchronously.
#Override
public <T> T method(final String name, final Class<T> responseType)
throws ProcessingException, WebApplicationException {
// responseType null check omitted for brevity
requestContext.setMethod(name);
return new JerseyInvocation(this).invoke(responseType);
}
JerseyInvocation#invoke(Class<T>)
Synchronously invoke the request and receive a response of the specified type back.
#Override
public <T> T invoke(final Class<T> responseType)
throws ProcessingException, WebApplicationException {
// responseType null check omitted for brevity
final ClientRuntime runtime = request().getClientRuntime();
final RequestScope requestScope = runtime.getRequestScope();
return requestScope.runInScope(new Producer<T>() {
#Override
public T call() throws ProcessingException {
try {
return translate(runtime.invoke(requestForCall(requestContext)),
requestScope, responseType);
} catch (final ProcessingException ex) {
// Exception handling omitted for brevity
}
}
});
}
JerseyInvocation#translate(ClientResponse, RequestScope, Class<T>)
If the request suceeded, the response entity is read as an instance of specified Java type using Response#readEntity(Class<T>):
private <T> T translate(final ClientResponse response, final RequestScope scope,
final Class<T> responseType) throws ProcessingException {
if (responseType == Response.class) {
return responseType.cast(new InboundJaxrsResponse(response, scope));
}
if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
try {
return response.readEntity(responseType);
} catch (final ProcessingException ex) {
// Exception handling omitted for brevity
}
} else {
throw convertToException(new InboundJaxrsResponse(response, scope));
}
}