nifi disabled controller-services rest api call failed - java

I'm using spring 4.2.0 and nifi 0.6.0, when i use RestTemplate call restful api for disabled nifi's controller-services
//get nifi revision
public RevisionDTO getRevision() {
Entity revision = restTemplate.getForObject("http://localhost:8083/nifi-api//controller/revision", Entity.class);
return revision.getRevision();
}
//http parameter
protected Map<String, Object> getRevisionUrl(RevisionDTO revision) {
Map<String, Object> urlVar = new HashMap<String, Object>();
urlVar.put("version", revision.getVersion());
urlVar.put("clientId", revision.getClientId());
return urlVar;
}
//disable controller service
private void disable(ControllerServiceEntity cs) {
Map<String, Object> urlVar = getRevisionUrl(getRevision());
urlVar.put("state", STATE_DISABLED);
restTemplate.put("http://localhost:8083/nifi-api/controller/controller-services/node/" + cs.getControllerService().getId(), null, urlVar);
}
i got the following error log:
org.springframework.web.client.HttpClientErrorException: 409 Conflict
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:636)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:592)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:560)
nifi log
2016-04-11 16:32:53,982 DEBUG [NiFi Web Server-90 - /nifi-api/controller/controller-services/node/5d99ca33-78f6-465e-9ab1-8c7cb5650025] o.a.n.w.StandardOptimisticLockingManager Revision check failed because current revision is [23, 5d4fe4d3-2f32-4a75-899a-458e8e62efce] but supplied revision is [null, 15976fcf-e01b-483c-994b-df812293d7ad]
......
2016-04-11 16:32:53,990 DEBUG [NiFi Web Server-90 - /nifi-api/controller/controller-services/node/5d99ca33-78f6-465e-9ab1-8c7cb5650025] c.s.j.spi.container.ContainerResponse Mapped exception to response: 409 (Conflict)
org.apache.nifi.web.InvalidRevisionException: This NiFi instance has been updated by 'anonymous'. Please refresh to synchronize the view.
at org.apache.nifi.web.StandardOptimisticLockingManager.checkRevision(StandardOptimisticLockingManager.java:62) ~[nifi-web-optimistic-locking-0.6.0.jar:0.6.0]
at org.apache.nifi.web.StandardOptimisticLockingManager.configureFlow(StandardOptimisticLockingManager.java:80) ~[nifi-web-optimistic-locking-0.6.0.jar:0.6.0]
at org.apache.nifi.web.StandardNiFiServiceFacade.updateControllerService(StandardNiFiServiceFacade.java:1714) ~[classes/:0.6.0]
at org.apache.nifi.web.StandardNiFiServiceFacade$$FastClassBySpringCGLIB$$358780e0.invoke() ~[classes/:0.6.0]
browser call disabled api screenshot
is there any way to fix the error?
#Matt Gilman i have changed disable function in two ways:
private void disable(ControllerServiceEntity cs) {
//http form
/*MultiValueMap<String, String> urlVar = getRevisionUrl(getRevision());
urlVar.add("state", STATE_DISABLED);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(urlVar, requestHeaders);*/
//http json
ControllerServiceEntity csu = new ControllerServiceEntity();
csu.setRevision(getRevision());
csu.setControllerService(cs.getControllerService());
csu.getControllerService().setState(STATE_DISABLED);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<String>(JSONUtils.obj2json(csu), requestHeaders);
restTemplate.put(url() + cs.getControllerService().getId(), requestEntity);
}
but i got the same error in nifi:
java.lang.IllegalStateException: DBCPConnectionPool[id=fecc88a8-379d-4fb3-b880-28c5f5caef23] cannot be updated because it is not disabled
at org.apache.nifi.controller.service.StandardControllerServiceNode.verifyCanUpdate(StandardControllerServiceNode.java:217) ~[nifi-framework-core-0.6.0.jar:0.6.0]
at org.apache.nifi.web.dao.impl.StandardControllerServiceDAO.verifyUpdate(StandardControllerServiceDAO.java:225) ~[classes/:na]
at org.apache.nifi.web.dao.impl.StandardControllerServiceDAO.updateControllerService(StandardControllerServiceDAO.java:101) ~[classes/:na]
solved
i compare browser disable request and read api doc again, found disable a controller service must follow these steps:
stop controller service reference
disable controller service reference
set controller service disable state
now my new disable function is
private void disable(ControllerServiceEntity cs) {
//http form
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//stop controller service reference
MultiValueMap<String, String> urlVar = getRevisionUrl(getRevision());
urlVar.add("state", STATE_STOPPED);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(urlVar, requestHeaders);
restTemplate.put(url() + cs.getControllerService().getId() + "/references", requestEntity);
//disable controller service reference
urlVar = getRevisionUrl(getRevision());
requestEntity = new HttpEntity<MultiValueMap<String, String>>(urlVar, requestHeaders);
urlVar.add("state", STATE_DISABLED);
restTemplate.put(url() + cs.getControllerService().getId() + "/references", requestEntity);
//set controller service disable state
urlVar = getRevisionUrl(getRevision());
requestEntity = new HttpEntity<MultiValueMap<String, String>>(urlVar, requestHeaders);
urlVar.add("state", STATE_DISABLED);
restTemplate.put(url() + cs.getControllerService().getId(), requestEntity);
}

It appears the revision is not being interpreted when your request is received. I've seen this once before when the Content-Type of the request was not set correctly and as a result, the revision was not properly parsed.
I'm not super familiar with RestTemplate but the request coming from your browser's Developer Tools has a Content-Type of application/x-www-form-urlencoded. Can you check that RestTemplate is issuing the same type of request?
Alternatively, you could issue the request using the Object's directly and use application/json. While a little different here is an example of setting Processor scheduled state using the client DTOs [1].
Also, at the bottom of this article is a great sequence diagram of the revision use case [2].
[1] https://github.com/apache/nifi/blob/master/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/accesscontrol/DfmAccessControlTest.java#L423
[2] https://community.hortonworks.com/content/kbentry/3160/update-nifi-flow-on-the-fly-via-api.html

Related

Bad request with RestTemplate -> postForObject (Spring Boot)

I'm experiencing some troubles with a simple matter.
I'm trying to send a request to other REST service
//getting restTemplate from RestTemplateBuilder.build()
//endpoint and rest of variables came in properties
Map<String, String> map = new HashMap<>();
map.put("app", app);
map.put("username", username);
map.put("password", password);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
String token = restTemplate.postForObject(loginEndpoint, headers, String.class, map);
And I recive:
Unexpected error occurred in scheduled task.
org.springframework.web.client.HttpClientErrorException: 400 Bad Request
The weird thing, when I use a simple CURL call and works smooth.
Already checked the variables and endpoint, and it's correct.
In this case, endpoint must have appropiate placeholders on end point url.
I made this method to do it easy:
private String placeHolders(Map<String, String> values){
String response = "?";
boolean first = true;
for(Map.Entry<String, String> entry:values.entrySet()){
if(first){
first = false;
}else{
response+="&";
}
response+=entry.getKey()+"="+entry.getValue();
}
return response;
}
And the call now Is:
String token = restTemplate.postForObject(loginEndpoint+placeHolders, headers, String.class, map);

Spring boot - restTemplate.postForObject - params are null

I have 2 spring boot apps running one as a frontend and another as a backend service. From the frontend i make an api call to the backend service and both the parameters that i send show up as null. I think the problem is in the rest template.
UPDATE
So i have noticed if i omit the content value then it works. Since content is the content of a file that is larger than 1mb I added the following to application.yml:
spring.servlet.multipart.max-file-size: 10MB
spring.servlet.multipart.max-request-size: 10MB
Here is my code which I updated from one posted in this issue:
How to POST form data with Spring RestTemplate?
But i still don't get the value in the backend controller instead both values are null.
public void upload(byte[] content, String name) {
String encodedString = Base64.getEncoder().encodeToString(content);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("fileName", name);
map.add("content", encodedString);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<String> response = restTemplate.postForEntity(backendUrl + "/upload", request, String.class);
log.debug("Response from upload: " + response);
}
And here is the controller in the backend. Both fileName and content are null:
#CrossOrigin
#SneakyThrows
#ResponseBody
#PostMapping(value = "/upload")
public ResponseEntity<String> upload(#ModelAttribute FormModel form) {
byte[] decodedBytes = Base64.getDecoder().decode(form.getContent());
uploadService.upload(decodedBytes, form.getFileName());
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Content-Type", "application/json");
return ResponseEntity.ok().headers(responseHeaders).body("Uploaded");
}
Can anyone please see what is wrong with this code?
Thanks in advance.
I guess the problem is that you are trying to use restTemplate.postForObject but with #RequestParam and not a #RequestBody.
In #RequestParam you are expecting the data to be received in the query params /upload?fileName=&content=. But you are actually sending it in the body with the restTemplate.postForObject(backendService+ "/upload", map, String.class);.
So my suggestion is to change
public ResponseEntity<String> upload(#RequestParam(value = "fileName") String fileName, #RequestParam(value = "content") String content)
to
public ResponseEntity<String> upload(#RequestBody Map<String, String> body)
and then get fileName and fileContent from the body.
Ok i could fix it by sending and receiving bytes instead of bytes encoded as string.
So in the resttemplate:
public void upload(byte[] bytes, String name) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
map.add("fileName", name);
map.add("bytes", bytes);
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(map, headers);
log.debug("map values: " + map.toString());
ResponseEntity<String> response = restTemplate.postForEntity(backendUrl + "/upload", request, String.class);
log.debug("Response from upload: " + response);
}
And in the controller:
public ResponseEntity<String> upload(#ModelAttribute FormModel form) {
byte[] bytes = form.getBytes();
uploadService.upload(bytes, form.getFileName());
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Content-Type", "application/json");
return ResponseEntity.ok().headers(responseHeaders).body("Uploaded");
}
Still it would be good to know why the previous version didn't work.

How to upload multipart file through rest api?

I know how to upload multipart file from postman, but how to do the same through REST API. The consumer API works fine when I hit through postman, but while doing the same through REST, it does not work.
Same thing I am doing through REST like this but its not working:
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://localhost:9001/communication/api/messageEngine/event/RECIPT/sendEmailAttachment";
ParameterizedTypeReference<ApiResponse<Map<String,Object>>> parameterizedTypeReference =
new ParameterizedTypeReference<com.loylty.dataacquisition.model.ApiResponse<Map<String,Object>>>() {};
RestTemplate restTemplate = new RestTemplate();
try {
ResponseEntity<com.loylty.dataacquisition.model.ApiResponse<Map<String,Object>>> result =
restTemplate.exchange(serverUrl, HttpMethod.POST, requestEntity, parameterizedTypeReference);
if (result.getStatusCode().is2xxSuccessful() == false) {
throw new DENotReachableException();
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
Target API or consumer API:
#CrossOrigin
#RequestMapping(method = RequestMethod.POST, value = "/event/{event}/sendEmailAttachment", consumes = {"multipart/form-data"})
public ApiResponse<Object> sendReceiptWithAttachment(#RequestPart("file") MultipartFile file, #PathVariable("event") String event) {
LidsysUtil.messageId.set(String.valueOf(new Date().getTime()));
MessageTracker tracker = new MessageTracker(LidsysUtil.messageId.get(), event);
LidsysUtil.tracker.set(tracker);
LOGGER.info("Executing Message Id : {} ", LidsysUtil.messageId.get());
LOGGER.info("Request received for event : {}", event);
// LOGGER.info("Request Body : {}", LidsysUtil.displayJSON(requestBody));
Map<String, Object> request = messageEngineService.initiateEmailwithAttachmentV2( file, event);
return new ApiResponse<>(APIResponseKey.ALL_GOOD, messageEngineService.execute(request, event), null);
}
Following exception I get when I try with REST api
Exception on source microservice
020-10-21 19:11:18,237 [ERROR]---[DirectJDKLog.java]---[http-nio-8010-exec-2]: Servlet.service() for servlet [dispatcherServlet] in context with path [/dataacquisition] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 400 null] with root cause
org.springframework.web.client.HttpClientErrorException: 400 null
Exception on target microservice
2020-10-21 19:11:17,445 [ERROR]---[HttpLoggingFilter.java]---[http-nio-9001-exec-10]: null
When you are instantiating the ParameterizedTypeReference object why do you refer to the ApiResponse in two different ways? (You use both the simple name and later the full qualified name com.loylty.dataacquisition.model.ApiResponse) Could that mean that these may be two different classes?
Also, on the consumer side, you have declared the sendReceiptWithAttachment() method to return ApiResponse<Object>, rather than ApiResponse<Map<String,Object>>.
Make sure both the consumer and the producer are in sync.
I found the solution, here in map I was adding File instead it required of type Resource
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file", file);
Solution
Resource rfile = new FileSystemResource(file)
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file", rfile );

How to get and parse JSON response from x-www-form-urlencoded POST, RestTemplate (Java)?

I have this method to make request:
#Override
public HttpEntity<MultiValueMap<String, String>> generateRequestEntity(Date date) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("key", "SOME_API_KEY");
map.add("source", "SOME_API_SOURCE");
if (date == null)
map.add("method", "getall");
else {
map.add("method", "getfrom");
map.add("date", new SimpleDateFormat("yyyy-MM-dd").format(date));
}
return new HttpEntity<>(map, headers);
}
I send a request and get a response, as recommended at following link: URL
HttpEntity<MultiValueMap<String, String>> request = generateRequestEntity(date);
ResponseEntity<OnlineSell[]> response = restTemplate.postForEntity(url, request, OnlineSell[].class);
OnlineSell[] onlineSells = response.getBody();
But I have a problem. I am failing when trying to parse JSON-response. OnlineSell - class, that must keep the answer BUT I just can’t create a structure that successfully stores the values ​​of this answer. A very large and complex answer tree comes.
Answer: Can I get JSONObject from it to parse and save manually? Or can I get help with JSON parsing by previously updating this post and adding it (answer form Postman)?
What you can do is to consider the ResponseEntity as a String.
Then afterwards you can use objectMapper with readValue(),
Something like this:
ResponseEntity<String> response = restTemplate().postForEntity(url, request, String.class);
String body = response.getBody();
OnlineSell[] onlineSells = new ObjectMapper().readValue(body, OnlineSell[].class);

unable to post large files using spring rest template w/ custom interceptor

I am trying to POST a large file from one microservice to another using spring rest template POST w/ custom interceptor as follows:
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate = new RestTemplate(requestFactory);
restTemplate.getInterceptors().add({customInterceptor});
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", {InputStreamResource});
body.add("metadata", {JSON string});
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
restTemplate.exchange({url}, HttpMethod.POST, requestEntity, ...);
(It makes no difference whether I use a SimpleClientHttpRequestFactory or HttpComponentsClientHttpRequestFactory)
Adding an interceptor results in the creation of an new InterceptingClientHttpRequestFactory (which wraps the original request factory) on a call to getRequestFactory.
This works fine for smaller files but for large files - since requests are never delegated to the original request factory, no streaming ever occurs and, hence, results in a java.lang.OutOfMemoryError: Java heap space exception.
Any help would be appreciated.
It seems this problem of RestTemplate will not be fixed according to this issue
we're not going to support this, now that the WebClient is available and provides first-class support for streaming.
Note that WebClient is available in Spring 5.
I have also come across this issue, but switching to WebClient is not an option at this point in time so I opted for a work around by implementing a new ClientHttpRequestFactory (albeit via an AbstractClientHttpRequestFactoryWrapper); it mainly works due to the fact that we are only tweaking the headers and not the body.
SimpleClientHttpRequestFactory simpleRequestFactory =
new SimpleClientHttpRequestFactory();
simpleRequestFactory.setConnectTimeout(10000);
simpleRequestFactory.setReadTimeout(60000);
simpleRequestFactory.setBufferRequestBody(false); // this enables streaming
SomeHeaderInterceptingClientHttpRequestFactory interceptingRequestFactory =
new SomeHeaderInterceptingClientHttpRequestFactory(simpleRequestFactory);
RestTemplate restTemplate = new RestTemplate(interceptingRequestFactory);
and this is what the SomeHeaderInterceptingClientHttpRequestFactory looks like:
public class SomeHeaderInterceptingClientHttpRequestFactory
extends AbstractClientHttpRequestFactoryWrapper {
// you can have fields here that are initialized in the constructor
// e.g. a service that supplies the header value that you want to populate
public SomeHeaderInterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
// initialize fields
}
#Override
protected ClientHttpRequest createRequest(URI uri,
HttpMethod httpMethod,
ClientHttpRequestFactory requestFactory)
throws IOException {
ClientHttpRequest request = requestFactory.createRequest(uri, httpMethod);
HttpHeaders headers = request.getHeaders();
headers.set("SOME_HEADER", "some value");
return request;
}
}
If you want to do something to the body then you could also try to implement a new ClientHttpRequest. You will then return a new instance of your newly introduced ClientHttpRequest in the createRequest method of your newly introduced ClientHttpRequestFactory.

Categories

Resources