I am trying to use OAuth2RestOperations to call an api like below and getting "java.lang.IllegalArgumentException: URI is not absolute" exception. What am I missing?
public void sendSMS(String message, String destination) {
String messageUrl = "http://baseurl/uri"
System.out.println("Message url: " + messageUrl);
JSONObject messageObject = new JSONObject()
.put("message", message)
.put("destination", destination);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
headers.add("Accept", "application/json");
HttpEntity<Object> request = new HttpEntity<>(messageObject, headers);
try {
ResponseEntity<String> exchange = restTemplate
.exchange(URI.create(messageUrl), HttpMethod.POST, request, String.class);
System.out.println("Response is: " + exchange.getBody());
} catch (Exception e) {
e.printStackTrace();
}
Found the issue. While configuring the OAuthTemplate I missed to provide the full url.
#Bean
public OAuth2ProtectedResourceDetails clientCredentialsResourceDetails() {
ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails();
resource.setAccessTokenUri(
"http://baseurl/token") resource.setGrantType("client_credentials");
resource.setClientId(sapProperties.getAppKey());
resource.setClientSecret(sapProperties.getAppSecret());
return resource;
}
Related
I'm trying to generate a token from an external website, the post request must be application/x-www-form-urlencoded but I'm getting errors. I assume that I'm not making the right call for the content type application/x-www-form-urlencoded but I can't figuer out how!!
PS: I'm using springboot with java 8
here is the code:
public String getNewAccesToken() {
//Initilazing variabels
try {
JsonObject properties = new JsonObject();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.set("Cookie", cookie);
properties.addProperty("client_id", clientId);
properties.addProperty("client_secret", clientSecret);
properties.addProperty("grant_type", grantType);
HttpEntity<String> requestEntity = new HttpEntity<>(properties.toString(), headers);
log.debug(requestEntity);
ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.POST, requestEntity,
String.class);
log.debug("---------------------------------");
log.debug(response);
if (Util.isJSONValid(response.getBody())) {
JsonParser parser = new JsonParser();
JsonObject jsonString = (JsonObject) parser.parse(response.getBody());
return jsonString.get("accessToken").getAsString();
} else {
error.setCode(ConstantGateway.JSON_ERROR_CODE);
error.setMessage(ConstantGateway.JSON_ERROR_STATUS);
return error.toString();
}
} catch (HttpStatusCodeException e) {
error.setCode(String.valueOf(e.getStatusCode().value()));
error.setMessage(e.getResponseBodyAsString());
return error.toString();
} catch (Exception e) {
// TODO: handle exception
error.setCode(ConstantGateway.IL_INTERNAL_ERROR_CODE);
error.setMessage(e.getMessage());
return error.toString();
}
}
and here is what I get when calling this function:
org.zwz.vas.internal.api.model.ErrorModel#16dd359c
However when I make the call from Postman I get the right response which is :
{
"access_token": "RdWt3DNIfxmihnubGX0Fgfcb0KNHLZV79OfN9Y6Ky6Z3fxAfF_Pm7uP0jnFrG1fHplyBMZ74BIKleQ8jmswdGy4e87NV-uZsMzgS1nQAONc2nBxgU1_jkMBhL4vvIniJNd99oYNzGeanCYYki0yorrrlLrOGTncusv1BgFFHU_CBGuUtGmZYLfJAJW4XcZLhXMC9xpT2aWAvgRXZW69pOhfU1Fgs7aVwou85UVI2b4j1GfX0pCtJtluiTgXsuWqdck7_at1dqfopHpjWAywYrweStMXGm8T59nyQi_oXWmo",
"token_type": "bearer",
"expires_in": 1199
}
THANK YOU IN ADVANCE
I find the mistake I did, I was sending a wrong request format which is not compatibale with application/x-www-form-urlencoded type. The right code is below:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.set("Cookie", cookie);
MultiValueMap<String, String> properties= new LinkedMultiValueMap<String, String>();
properties.add("client_id", clientId);
properties.add("client_secret", clientSecret);
properties.add("grant_type", grantType);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(properties, headers);
ResponseEntity<String> response = restTemplate.postForEntity( requestUrl, request , String.class );
Right now I have 2 Spring App.
App A will have a controller that will receive a video file ad Multipart file and sending the file to App B via rest template.
Some Code from App A that handle sending request to App B.
#RestController
public class AppAController {
#Autowired
private final AppBService service;
#PostMapping("/sendToB")
public ResponseEntity<String> contoller(#RequestParam("file") MultipartFile file) {
String result = service.sendToB(file);
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
#Service
public class AppBService {
public String sendToB(MultipartFile file) throws ResponseStatusException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", file);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String serverUrl = "http://127.0.0.1:8090/makeFrames";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
serverUrl,
HttpMethod.POST,
requestEntity,
String.class
);
if (response.getStatusCode() != HttpStatus.OK) {
throw new ResponseStatusException(response.getStatusCode(), response.getBody());
}
return response.getBody();
}
}
And for App B, it will receive a video and extracting key frames from a video using JavaCV.
#RestController
public class ProcessorController {
#PostMapping("/makeFrames")
public ResponseEntity<String> framesExtractorController(#RequestParam("file") MultipartFile file) {
try {
File result = FramesExtractor.grabFrames(file);
return new ResponseEntity<>(result.getAbsolutePath(), HttpStatus.OK);
} catch (IOException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
}
public class FramesExtractor {
private static final Logger LOG = LoggerFactory.getLogger(FramesExtractor.class);
private FramesExtractor() {
}
public static File grabFrames(MultipartFile video) throws IOException {
LOG.info("Extracting Frames from the video " + video.getName());
String directoryName = video.getOriginalFilename() + "-frames-result";
directoryAssurance(directoryName);
try (FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(video.getInputStream());
Java2DFrameConverter converter = new Java2DFrameConverter()) {
frameGrabber.setImageWidth(480);
frameGrabber.setImageHeight(360);
frameGrabber.start();
Frame frame;
int i = 0;
while ((frame = frameGrabber.grabKeyFrame()) != null) {
BufferedImage bi = converter.getBufferedImage(frame);
ImageIO.write(bi, "png", new File(directoryName + "/" + String.format("%03d", i) + ".png"));
i++;
}
frameGrabber.stop();
LOG.info("Finish Extracting Frames");
return new File(directoryName);
} catch (Exception e) {
LOG.error(e.getMessage());
throw e;
}
}
}
Both app A and B have these properties set to their application.properties files.
server.port={There respective port}
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
And when i testing the application by using PostMan to send post request with video file in form-data body to App A /sendToB api i receive this error message.
"Type definition error: [simple type, class java.io.FileDescriptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile[\"inputStream\"]->java.io.FileInputStream[\"fd\"])"
So I want to ask what cause this problems and how I can fix it or sending video file to other Spring App running on other port properly.
I edited my previous answer because you can actually get parameters from a response body in a POST with #RequestParam annotation.
I would use JSON as content type. This is my solution:
public String sendToB(MultipartFile file) throws ResponseStatusException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
byte[] bytesFile = file.getBytes();
String base64String = Base64.getEncoder().encodeToString(bytesFile);
Map<String, Object> body = new HashMap<>();
body.put("file", base64String);
body.put("directoryName", file.getOriginalFilename());
body.put("videoName", file.getName())
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String serverUrl = "http://127.0.0.1:8090/makeFrames";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
serverUrl,
HttpMethod.POST,
requestEntity,
String.class
);
if (response.getStatusCode() != HttpStatus.OK) {
throw new ResponseStatusException(response.getStatusCode(), response.getBody());
}
return response.getBody();
}
For App B, the code would be:
#PostMapping("/makeFrames")
public ResponseEntity<String> framesExtractorController(#RequestBody Map<String, Object> requestBody) {
try {
String base64String = requestBody.get("file").toString();
byte[] bytesFile = Base64.getDecoder().decode(base64String);
InputStream inputStream = new ByteArrayInputStream(bytesFile);
String directoryName = requestBody.get("directoryName").toString();
String videoName = requestBody.get("videoName").toString();
File result = FramesExtractor.grabFrames(inputStream, directoryName, videoName);
return new ResponseEntity<>(result.getAbsolutePath(), HttpStatus.OK);
} catch (IOException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
}
public static File grabFrames(InputStream inputStream, String directoryName, String videoName) throws IOException {
//Your code
}
#HystrixCommand(commandKey = "MyHystrixCommand", fallbackMethod = "myHystrixFallback", threadPoolKey = "ThreadPoolKey")
public ResponseEntity<String> getServiceCallResponse(String serviceUrl, HttpEntity<?> entity) {
ResponseEntity<String> resp = null;
try {
resp = restTemplate.exchange(serviceUrl, HttpMethod.POST, entity, String.class)
.getBody();
}
catch(Exception e) {
handleExceptionForHystrix("getServiceCallResponse", e);
}
return resp;
}
Getting error when setting resp saying it's a mismatch. Unable to understand why it's going on. What can be the alternative.
I am using Spring RestTemplate and want to make a call to another service that doesn't return any response body. So, I don't want to wait for the response. So, it's just fire and forget, and continue with the remaining code. I am thinking of creating a new Thread to do this but really not sure what's the correct approach.
If you use Java 11, java support asynchronous HTTP Client. Asynchronous client using CompletableFuture in the back. You can see javadoc.
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://openjdk.java.net/"))
.timeout(Duration.ofMinutes(1))
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofFile(Paths.get("file.json")))
.build();
client.sendAsync(request, BodyHandlers.ofString())
.thenApply(response -> { System.out.println(response.statusCode());
return response; } )
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
The correct approach is to execute the async with a callback (using DeferredResult, like this (assuming we have a class someClass that we want to retrieve from the API:
#GetMapping(path = "/testingAsync")
public DeferredResult<String> value() throws ExecutionException, InterruptedException, TimeoutException {
AsyncRestTemplate restTemplate = new AsyncRestTemplate();
String baseUrl = "http://someUrl/blabla";
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
String value = "";
HttpEntity entity = new HttpEntity("parameters", requestHeaders);
final DeferredResult<String> result = new DeferredResult<>();
ListenableFuture<ResponseEntity<someClass>> futureEntity = restTemplate.getForEntity(baseUrl, someClass.class);
futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<someClass>>() {
#Override
public void onSuccess(ResponseEntity<someClass> result) {
System.out.println(result.getBody().getName());
result.setResult(result.getBody().getName());
}
#Override
public void onFailure(Throwable ex) {
result.setErrorResult(ex.getMessage());
}
});
return result;
}
There are many ways you can use to fire the request using the AsyncRestTemplate
The simplest way is just like restTemplate and call exchange method:
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
JSONObject json = new JSONObject();
json.put("firstName","testUser");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<String>(json.toString(), headers);
Class<String> responseType = String.class;
ListenableFuture<ResponseEntity<String>> future = asyncRestTemplate.exchange("https://xxxxx.com/", HttpMethod.POST, requestEntity,responseType );
// If you want for the result then you can use
try {
//waits for the result
ResponseEntity<String> entity = future.get();
//prints body source code for the given URL
log.info(entity.getBody());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
If we want to play with the failure (fallback scenario) or success in that case we can use the below code :
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
JSONObject json = new JSONObject();
json.put("firstName","testUser");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<String>(json.toString(), headers);
//final DeferredResult<String> result = new DeferredResult<>();
ListenableFuture<ResponseEntity<String>> future =
asyncRestTemplate.postForEntity("https://xxxx.com", requestEntity, String.class);
future.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
#Override
public void onFailure(Throwable ex) {
// insert into the table or log or some other decision
log.info(ex.getMessage());
}
#Override
public void onSuccess(ResponseEntity<String> result) {
log.info(result.getBody());
log.info("Sucess");
}
});
I want to use Spring RestTemplate instead of Apache HttpClient for working with a remote API
With HttpClient
// build request JSON
JSONObject json = new JSONObject();
json.put("username", username);
json.put("serial", serial);
json.put("keyId", keyId);
json.put("otp", otp);
String json_req = json.toString();
// make HTTP request and get response
HttpPost request = new HttpPost(AuthServer);
request.setHeader("Content-Type", "application/json");
request.setEntity(new StringEntity(json_req));
response = client.execute(request);
With RestTemplate
Map<String, String> paramMap = new HashMap<String,String>();
paramMap.put("username", userName);
paramMap.put("serial", serial);
paramMap.put("keyId", keyId);
paramMap.put("otp", otp);
String mapAsJson = new ObjectMapper().writeValueAsString(paramMap);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<String>(mapAsJson,requestHeaders);
try {
ResponseEntity<String> response = restTemplate.exchange(AuthServer, HttpMethod.POST, request, String.class);
return response.getHeaders();
} catch (HttpClientErrorException e) {
return null;
}
}
The code with HttpClient works but that with RestTemplate does not. I don't know how to use StringEntity in RestTemplate.
Spring version is 3.0.0, and JVM is 1.6.
RestTemplate is better suited to working with objects. As an example:
AuthenticationRequest.java
class AuthenticationRequest {
private String username;
private String serial;
private String key;
private String otp;
}
AuthenticationResponse.java
class AuthenticationResponse {
private boolean success;
}
AuthenticationCall.java
class AuthenticationCall {
public AuthenticationResponse execute(AuthenticationRequest payload) {
HttpEntity<AuthenticationRequest> request = new HttpEntity<AuthenticationRequest>(payload, new HttpHeaders());
return restTemplate.exchange("http://www.domain.com/api/endpoint"
, HttpMethod.POST
, request
, AuthenticationResponse.class).getBody();
}
}
These classes can be used as follows:
if(new AuthenticationCall().execute(authenticationRequest).isSuccess()) {
// Authentication succeeded.
}
else {
// Authentication failed.
}
All of this requires there to be a JSON library such as Jackson or GSON on the classpath.