Trouble sending the Jwt in the request header - retrofit2

I'm trying to get a response from a simple protected Endpoint in the back end.
I've tested the Endpoint in Postman. I set up GET request with the KEY: Authorization and a VALUE: bearer eyxhsls...(this is the Jwt)
and the response gives me Status: 200 OK and the requested String. So everything works fine in the back end.
Now I want to replicate this process on the Client-side using Retrofit. Based on some research I using an OkHttpClient.Builder to insert the Jwt(String) into the header.
I try different things like simply inserting the Jwt(String) into header value:
Request.Builder newRequest = request.newBuilder().header("Authorization", bearerToken);
This returns a 401 status
I have also added the "Bearer " to the VALUE, just like I did in Postman, but the caller referred me to the onFailure method with the Message:
use jsonreader.setlenient(true) to accept malformed json at line 1 column 1
So I have added a GsonBuilder with setLeniet to the addConverterFactory. the caller again referred me to the onFailure method, but with the Message:
JSON document was not fully consumed.
Plz, let me know if anyone has a better idea, or understands what is going on. But stuck now for a number of days now.
public CoffeeController() {
okhttpBuilder = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#NonNull
#Override
public okhttp3.Response intercept(#NonNull Chain chain) throws IOException {
Request request = chain.request();
bearerToken = "Bearer " +LoginController.getToken();
bearerToken = LoginController.getToken();
Request.Builder newRequest = request.newBuilder().header("Authorization", bearerToken);
return chain.proceed(newRequest.build());
}
});
gson = new GsonBuilder()
.setLenient()
.create();
retrofit = new Retrofit.Builder()
.baseUrl("http://10.0.2.2:8080/")
.client(okhttpBuilder.build())
//.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
public static void CoffeeRead(Context context, TextView ResponseView) {
try {
CoffeeRepo repo = retrofit.create(CoffeeRepo.class);
Call<String> call = repo.Read();
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
message = "Read Coffee: " +"\nToken: " +bearerToken +"\nResponse: " + response.code();
ResponseView.setText(message);
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(Call<String> call, Throwable t) {
message = "Failed to read coffee: \n" + t.getMessage();
ResponseView.setText(message);
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
});
} catch (Exception e) {
message = "Caught Exception: \n" + e.getMessage();
ResponseView.setText(message);
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
}
public interface CoffeeRepo {
#Headers({
"Cache-Control: max-age=3600",
"User-Agent: Android"
})
#GET("coffee")
Call<String> Read();
}

After I've added a logger, I found out the response was 200. After some research I found out I needed different ConverterFactory, instead of:
.addConverterFactory(ScalarsConverterFactory.create())
I used
.addConverterFactory(GsonConverterFactory.create(gson));

Related

Spring Integration DSL - OAuth2ErrorHandler issues with 4XX series error codes

We are using spring integration DSL to call downstream services. But we are facing issues when 4XX series error code is returned by downstream service.
Below is the code snippet that we are using to call downstream services
#Bean
public IntegrationFlow getDataChannelFlow() {
return IntegrationFlows.from(MessageChannels.executor("getDataChannel", Executors.newCachedThreadPool()))
.enrichHeaders(h -> h.headerExpression(USER_REF, SRC_USER_REF)
.handle(Http.outboundGateway(endPoint1, auth2RestTemplate)
.uriVariable("data", PAYLOAD)
.httpMethod(HttpMethod.GET)
.transferCookies(true)
.expectedResponseType(String.class), e->e.advice(integrationAdvice).id("docIDAdvice"))
.transform(this::responseTransformer)
.enrichHeaders(header -> header.headerExpression("DOCUMENTS", PAYLOAD))
}
In case of 200 and 500 response from downstream services, our code is working fine but when we get 4XX series errors we are getting below exception in logs and control does not return back to transformer method
Caused by: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8080/fetchUser": Attempted read from closed stream.; nested exception is java.io.IOException: Attempted read from closed stream.
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:785)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:138)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:732)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:612)
at org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler.exchange(HttpRequestExecutingMessageHandler.java:196)
... 42 more
Caused by: java.io.IOException: Attempted read from closed stream.
at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:141)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:118)
Few things that we noticed while debugging -
Spring's OAuth2ErrorHandler.java class differentiates between 4XX and 5XX series of errors
public boolean hasError(ClientHttpResponse response) throws IOException
{
return HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series())
|| this.errorHandler.hasError(response);
}
In above code snippet hasError() method returns true for 4XX series of codes and due to this we are getting IOException when below code snippet is executed
protected <T> T doExecute(URI url, #Nullable HttpMethod method, #Nullable RequestCallback requestCallback,
#Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
Our expectation is that control should return back to transformer method so that we will have the control over response processing.
Any suggestions on this issue would be much appreciated.

Spring Integration Default Response for Jms inboundGateway

Seeing the below exception when trying to send a default constructed response for Jms inboundGateway exception from the downstream call. We are extracting the failedMessage headers from the ErrorMessage and then setting the constructed response as payload. The replyChannel headers is matching with the initially logged message header
2023-01-26 20:34:32,623 [mqGatewayListenerContainer-1] WARN o.s.m.c.GenericMessagingTemplate$TemporaryReplyChannel - be776858594e7c79 Reply message received but the receiving thread has exited due to an exception while sending the request message:
ErrorMessage [payload=org.springframework.messaging.MessageHandlingException: Failed to send or receive; nested exception is java.io.UncheckedIOException: java.net.SocketTimeoutException: Connect timed out, failedMessage=GenericMessage [payload=NOT_PRINTED, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#2454562d, b3=xxxxxxxxxxxx, nativeHeaders={}, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#2454562d, sourceTransacted=false, jms_correlationId=ID:xxxxxxxxxx, id=xxxxxxxxxx, jms_expiration=36000, timestamp=1674750867614}]
Code:
return IntegrationFlows.from(Jms.inboundGateway(mqGatewayListenerContainer)
.defaultReplyQueueName(replyQueue)
.replyChannel(mqReplyChannel)
.errorChannel(appErrorChannel)
.replyTimeout(mqReplyTimeoutSeconds * 1000L))
// log
.log(DEBUG, m -> "Request Headers: " + m.getHeaders() + ", Message: " + m.getPayload())
// transform with required response headers
.transform(Message.class, m -> MessageBuilder.withPayload(m.getPayload())
.setHeader(ERROR_CHANNEL, m.getHeaders().get(ERROR_CHANNEL))
.setHeader(REPLY_CHANNEL, m.getHeaders().get(REPLY_CHANNEL))
.setHeader(CORRELATION_ID, m.getHeaders().get(MESSAGE_ID))
.setHeader(EXPIRATION, mqReplyTimeoutSeconds * 1000L)
.setHeader(MSG_HDR_SOURCE_TRANSACTED, transacted)
.build())
return IntegrationFlows.from(appErrorChannel())
.publishSubscribeChannel(
pubSubSpec -> pubSubSpec.subscribe(sf -> sf.channel(globalErrorChannel))
.<MessagingException, Message<MessagingException>>
transform(AppMessageUtil::getFailedMessageWithoutHeadersAsPayload)
.transform(p -> "Failure")
.get();
public static Message<MessagingException> getFailedMessageAsPayload(final MessagingException messagingException) {
var failedMessage = messagingException.getFailedMessage();
var failedMessageHeaders = Objects.isNull(failedMessage) ? null : failedMessage.getHeaders();
return MessageBuilder.withPayload(messagingException)
.copyHeaders(failedMessageHeaders)
.build();
}
Since you perform the processing of the request message on the same thread, it is blocked on a send and therefore we just re-throw an exception as is:
try {
doSend(channel, requestMessage, sendTimeout);
}
catch (RuntimeException ex) {
tempReplyChannel.setSendFailed(true);
throw ex;
}
And as you see we mark that tempReplyChannel as failed on a send operation.
So, the replyChannel header correlated with that mqReplyChannel is out of use. If you get rid of it at all, then everything is OK. But you also cannot reply back an Exception since the framework treats it as an error to re-throw back to the listener container:
if (errorFlowReply != null && errorFlowReply.getPayload() instanceof Throwable) {
rethrow((Throwable) errorFlowReply.getPayload(), "error flow returned an Error Message");
}
So, here is a solution:
#SpringBootApplication
public class So75249125Application {
public static void main(String[] args) {
SpringApplication.run(So75249125Application.class, args);
}
#Bean
IntegrationFlow jmsFlow(ConnectionFactory connectionFactory) {
return IntegrationFlow.from(Jms.inboundGateway(connectionFactory)
.requestDestination("testDestination")
.errorChannel("appErrorChannel"))
.transform(payload -> {
throw new RuntimeException("intentional");
})
.get();
}
#Bean
IntegrationFlow errorFlow() {
return IntegrationFlow.from("appErrorChannel")
.transform(So75249125Application::getFailedMessageAsPayload)
.get();
}
public static Message<String> getFailedMessageAsPayload(MessagingException messagingException) {
var failedMessage = messagingException.getFailedMessage();
var failedMessageHeaders = failedMessage.getHeaders();
return MessageBuilder.withPayload("failed")
.copyHeaders(failedMessageHeaders)
.build();
}
}
and unit test:
#SpringBootTest
class So75249125ApplicationTests {
#Autowired
JmsTemplate jmsTemplate;
#Test
void errorFlowRepliesCorrectly() throws JMSException {
Message reply = this.jmsTemplate.sendAndReceive("testDestination", session -> session.createTextMessage("test"));
assertThat(reply.getBody(String.class)).isEqualTo("failed");
}
}
Or even better like this:
public static String getFailedMessageAsPayload(MessagingException messagingException) {
var failedMessage = messagingException.getFailedMessage();
return "Request for '" + failedMessage.getPayload() + "' has failed";
}
and this test:
#Test
void errorFlowRepliesCorrectly() throws JMSException {
String testData = "test";
Message reply = this.jmsTemplate.sendAndReceive("testDestination", session -> session.createTextMessage(testData));
assertThat(reply.getBody(String.class)).isEqualTo("Request for '" + testData + "' has failed");
}

How to access api.openrouteservice.org through Androids Volley package

I want to access data on the openrouteservice API - specifically the distance between two given coordinates on the globe - from my Android application.
I have made requests and gotten viable responses from another API that converts two given addresses into their latlong coordinates using the same style of code this request is trying to execute. It works fine, the coordinates arrive and i can further utilize them no problem.
My problem is that i seem to be accessing the API wrongly because if I Log the URL as seen below and copy it from the Debug window into my browser it sends the request, gets a response and shows it in the browser window.
But my application doesn't recieve a response from the API as the onResponse code bit is never executed and the "Fetch done" Log never appears in the actual Debug Log.
The following is my setup of code, which uses Volley to access HTTP Requests and which works fine for other APIs.
#Override
protected String doInBackground(String... strings) {
Log.d("Run =>","Query 3");
String targetKoordURL = null;
String startKoordURL = null;
try {
startKoordURL = startK.getString("lon").concat(",").concat(startK.getString("lat"));
targetKoordURL = targetK.getString("lon").concat(",").concat(targetK.getString("lat"));
} catch (JSONException e) {
e.printStackTrace();
}
String URLfin = "https://api.openrouteservice.org/v2/directions/driving-car?api_key=5b3ce3597851110001cf624823e587e7a80c4c6ab02af6d394585213&start="+startKoordURL+"&end="+targetKoordURL;
Log.d("Debug =>", URLfin);
JsonObjectRequest req = new JsonObjectRequest(Request.Method.GET, URLfin, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
store = response;
Log.d("Run =>", "Fetch done!");
continueImp();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if(error instanceof TimeoutError || error instanceof NoConnectionError){
sideFetcherHTTPRequestStart replace = new sideFetcherHTTPRequestStart();
replace.execute();
Log.d("VOLLEY_ERROR", "Retrying on Kilometer request");
}
error.printStackTrace();
}
}){
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Accept", "application/json,application/geo+json,application/gpx+xml,img/png; charset=utf-8");
return params;
}
};
return null;
}
You forget to add the request to request queue, try to do as following:
// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
JsonObjectRequest req = new JsonObjectRequest(/*params*/);
//add above request to queue
queue.add(req);

Error while processing request in AzureMobile Apps HTTP2 error

This question is specific to a lately strange behavior of the Azure mobile Apps Android sdk. Everything was working fine for weeks. Now, my android client app suddenly can't connect to my web app any more. A Toast says "Error while processing request". In Android Studio debugger, I found the exception inside the SDK file MobileServiceConnection.java.
java.io.IOException: stream was reset: PROTOCOL_ERROR
In Azure Portal, my app shows "Healthy" status, but I can see the HTTP errors. Please help.
Following is my code, which was working fine and now throws error.
// Create the Mobile Service Client instance, using the provided mobile app URL.
try {
mClient = new MobileServiceClient(mMobileBackendUrl, activityContext).withFilter(
new ServiceFilter() {
#Override
public ListenableFuture<ServiceFilterResponse> handleRequest(ServiceFilterRequest request, NextServiceFilterCallback nextServiceFilter) {
// Get the request contents
String url = request.getUrl();
String content = request.getContent();
if (url != null) {
Log.d("Request URL:", url);
}
if (content != null) {
Log.d("Request Content:", content);
}
// Execute the next service filter in the chain
ListenableFuture<ServiceFilterResponse> responseFuture = nextServiceFilter.onNext(request);
Futures.addCallback(responseFuture, new FutureCallback<ServiceFilterResponse>() {
#Override
public void onFailure(Throwable exception) {
Log.d("Exception:", exception.getMessage());
}
#Override
public void onSuccess(ServiceFilterResponse response) {
if (response != null && response.getContent() != null) {
Log.d("Response Content:", response.getContent());
}
}
});
return responseFuture;
}
}
);
setAzureClient(mClient);
}catch(MalformedURLException e){
createAndShowDialog(new Exception("There was an error creating the Mobile Service. Verify the URL"), "Error");
}catch(Exception e){
createAndShowDialog("There was an error creating the Mobile Service. "+ e.toString(), "Error");
}
Toast.makeText(context, context.getString(R.string.online_authentication), Toast.LENGTH_SHORT).show();
authenticate();
}
private void authenticate() { // give access only to authenticated users via Google account authentication
HashMap<String, String> parameters = new HashMap<>();
parameters.put("access_type", "offline");//use "Refresh tokens"
//login with the Google provider. This will create a call to onActivityResult() method inside the context Activity, which will then call the onActivityResult() below.
mClient.login(MobileServiceAuthenticationProvider.Google, url_scheme_of_your_app, GOOGLE_LOGIN_REQUEST_CODE, parameters);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// When request completes
if (requestCode == 1) {
try {
MobileServiceActivityResult result = mClient.onActivityResult(data);
if (result.isLoggedIn()) {
Toast.makeText(context, context.getString(R.string.azure_auth_login_success) /*+ " " + mClient.getCurrentUser().getUserId()*/, Toast.LENGTH_SHORT).show();
mUserId = mClient.getCurrentUser().getUserId();
} else {//>>>>THIS IS WHERE I AM GETTING THE ERROR
String errorMessage = result.getErrorMessage();
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show();// Error While processing request (it comes form the MobileServiceConnection.java file inside sdk)
}
}catch(Exception e){
Toast.makeText(context, e.toString(), Toast.LENGTH_LONG).show();
}
}
}
I found the answer myself. The error was due to an Azure App Service HTTP2 connection issue. It has nothing to do with the app code. For anyone facing the same problem, here is the solution.
Go to https://resources.azure.com/
Make sure you are in Read/Write mode by clicking in the option to the left of your name.
From the left column, browse to: https://resources.azure.com/subscriptions/yourSubscriptionId/resourceGroups/yourWebAppResourceGroup/providers/Microsoft.Web/sites/yourWebAppName/config/web
Find and Change the property: "http20Enabled": from true to false by clicking EDIT, Update value to “false” and then clicking in Save or PATCH.

Asynchronous programming in Java - Background processes

I have got a REST-API in my Java Web Application. This has a method to take orders from a customer's Android app (client) and send (after a bunch of tasks, like price calculating etc.) a response back to the client.
#POST
#Path("order")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public OrderResponse takeOrder(OrderRequest request
) throws IOException {
OrderResponse response = new OrderResponse();
String token = request.getTokenString();
CustomerSession session = sessionPool.getSession(token);
if (session != null) {
OrderHeader order = new OrderHeader();
order.setFkOrderHeaderCustomerID(session.getFkCustomerID());
order.setOrderCreationDate(new Date());
Tasks as getting the session for authentication etc. have to be done synchronously, sure. 'Cause the response for the clients depends on it's success or failure.. So far so good.
At the end of this method the client gets an email about the state of his order request.
Email email = EmailGenerator.createOrderEmail(order);
try {
emailService.send(email);
} catch (MessagingException ex) {
Logger.getLogger(CustomerREST.class.getName()).log(Level.SEVERE, null, ex);
}
response.setStatus(OrderStatusEnum.SUCCESS);
} else {
response.setStatus(OrderStatusEnum.TOKEN_INVALID);
}
return response;
}
This sometimes takes up to a few seconds for which the client has to wait for the response. That hurts.
Is there any way to send the response and do that email stuff in the background?
Thread mailingThread = new Thread() {
#Override
public void run() {
try {
Email email = EmailGenerator.createOrderEmail(order);
emailService.send(email);
} catch (MessagingException | IOException ex) {
Logger.getLogger(CustomerREST.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
mailingThread.start();
Thaks Kyle! This seems to do what I attempted!

Resources