webserver using spring boot kotlin in Corda with XML - spring

I am developing corda application using kotlin. I am on webserver spring boot. My requirment is to recieve(postman or any API from outside) XML in requestBody and create IOU(pass) that xml as it is to Flow but i am not able to so. when i define it as a string and pass that xml i am able to do it. but for xml i am struggling. Can anyone help please. Below is my code. am doing anything wrong here. The problem is- i dont get error but it just doesnt work.
#PostMapping(value = ["createTransaction"],consumes = [MediaType.APPLICATION_XML_VALUE],produces = [ MediaType.TEXT_PLAIN_VALUE])
private fun TransactionOne(#RequestBody ()employee:Document, #RequestParam(value = "payload") payload: String, #RequestParam(value = "partyName") partyName: String): ResponseEntity<String> {
val partyX500Name = CordaX500Name.parse(partyName)
val otherParty = proxy.wellKnownPartyFromX500Name(partyX500Name) ?: return ResponseEntity.badRequest().body("Party named $partyName cannot be found.\n")
return try {
val signedTx = proxy.startTrackedFlow(::IOUFlow, employee, otherParty).returnValue.getOrThrow()
ResponseEntity.status(HttpStatus.CREATED).body("Transaction id {$signedTx} committed to ledger.\n")
} catch (ex: Throwable) {
logger.error(ex.message, ex)
ResponseEntity.badRequest().body(ex.message!!)
}
}

This is not clear what does it mean "just doesn't work". You don't have response from "startTrackedFlow"?
What do you see in logs of your nodes? I think the answer is there.
There is not enough information to help you...

Related

Spring Traverson follow relative path ignores baseUri

I'm trying to use Traverson in my Spring Boot application to consume an external HAL-api. While doing so I get the exception org.apache.http.ProtocolException: Target host is not specified on the first hop, i.e. Traverson calls my baseUri without problems and with set target host, but seems to "forget" the host on the first hop.
And this is the code calling it:
override fun doStuff() {
// baseUri gets injected by class and is of form "https://my.host.com"
val startUri = UriComponentsBuilder.fromUriString(baseUri)
// If dms is declared as first hop, Traverson would call baseUri before first hop which returns a html website and results in an error -> dms has to be part of Traverson baseUri
.pathSegment("dms")
.build()
.toUri();
val authHeader = HttpHeaders()
// bearerToken gets injected by class and is not relevant for the problem
authHeader.set(HttpHeaders.AUTHORIZATION, bearerToken)
val restTemplate = RestTemplateBuilder()
.rootUri(baseUri)
.defaultHeader(HttpHeaders.AUTHORIZATION, bearerToken)
.build()
// httpRequestFactory gets injected by class and is used to configure SSL, should not matter for the problem
restTemplate.requestFactory = httpRequestFactory
val repoId: String = Traverson(startUri, MediaTypes.HAL_JSON)
.setRestOperations(restTemplate)
.follow("$._links.allrepos.href")
.withHeaders(authHeader)
.toObject("$.repositories[0].id")
log.debug("Repo id is $repoId")
}
This is the return of the Traverson baseUri (https://my.host.com/dms):
{
_links: {
allrepos: {
href: "/dms/r"
}
}
}
When executing this code, Traverson get's the link I'm searching for ("/dms/r") but when trying to follow it, it executes a call to "/dms/r" instead of "https://my.host.com/dms/r".
Am I missing something?
Every bit of help is very much appreciated!
EDIT 1:
It is possible to achieve my goal with following code. But if I have to resort to this option, I would instead use restTemplate directly without url discovery, since Traverson is just too inconvenient to use in this case.
val allReposLink = Traverson(startUri, MediaTypes.HAL_JSON)
.setRestOperations(restTemplate)
.follow("$._links.allrepos.href")
.withHeaders(headers)
.asLink()
.href
val allReposUri = UriComponentsBuilder.fromUriString(baseUri)
.pathSegment(allReposLink)
.build()
.toUri()
val repoId = Traverson(allReposUri, MediaTypes.HAL_JSON)
.setRestOperations(restTemplate)
.follow("$.repositories[0].id")
.withHeaders(headers)
.asLink()
.href
It should be possible to do it like this:
// DOES NOT WORK
val repoId: String = Traverson(startUri, MediaTypes.HAL_JSON)
.setRestOperations(restTemplate)
.follow("allrepos")
.withHeaders(headers)
.toObject("$.repositories[0].id")

How to write async data to remote endpoint without getting "No suitable writer found exception"?

I have the following controller method:
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, path = "/upload")
public Mono<SomeResponse> saveEnhanced(#RequestPart("file") Mono<FilePart> file) {
return documentService.save(file);
}
which calls a service method where I try to use a WebClient to put some data in another application:
public Mono<SomeResponse> save(Mono<FilePart> file) {
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
bodyBuilder.asyncPart("file", file, FilePart.class);
bodyBuilder.part("identifiers", "some static content");
return WebClient.create("some-url").put()
.uri("/remote-path")
.syncBody(bodyBuilder.build())
.retrieve()
.bodyToMono(SomeResponse.class);
}
but I get the error:
org.springframework.core.codec.CodecException: No suitable writer found for part: file
I tried all variants of the MultipartBodyBuilder (part, asyncpart, with or without headers) and I cannot get it to work.
Am I using it wrong, what am I missing?
Regards,
Alex
I found the solution after getting a reply from one of the contributes on the Spring Framework Github issues section.
For this to work:
The asyncPart method is expecting actual content, i.e. file.content(). I'll update it to unwrap the part content automatically.
bodyBuilder.asyncPart("file", file.content(), DataBuffer.class)
.headers(h -> {
h.setContentDispositionFormData("file", file.name());
h.setContentType(file.headers().getContentType());
});
If both headers are not set then the request will fail on the remote side, saying it cannot find the form part.
Good luck to anyone needing this!

How to mock a multipart file upload when using Spring and Apache File Upload

The project I'm working on needs to support large file uploads and know the time taken during their upload.
To handle the large files I'm using the streaming API of Apache FileUpload, this also allows me to measure the time taken for the complete stream to be saved.
The problem I'm having is that I cannot seem to be able to utilise MockMvc in an Integration Test on this controller. I know that the controller works as I've successfully uploaded files using postman.
Simplified Controller Code:
#PostMapping("/upload")
public String handleUpload(HttpServletRequest request) throws Exception {
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iterStream = upload.getItemIterator(request);
while (iterStream.hasNext()) {
FileItemStream item = iterStream.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (!item.isFormField()) {
// Process the InputStream
} else {
String formFieldValue = Streams.asString(stream);
}
}
}
Simplified Test Code:
private fun uploadFile(tfr: TestFileContainer) {
val mockFile = MockMultipartFile("file", tfr.getData()) // .getData*() returns a ByteArray
val receiveFileRequest = MockMvcRequestBuilders.multipart("/upload")
.file(mockFile)
.contentType(MediaType.MULTIPART_FORM_DATA)
val result = mockMvc.perform(receiveFileRequest)
.andExpect(status().isCreated)
.andExpect(header().exists(LOCATION))
.andReturn(
}
This is the error I'm currently getting
org.apache.tomcat.util.http.fileupload.FileUploadException: the
request was rejected because no multipart boundary was found
Can anyone help?
The MockMultipartFile approach won't work as Spring does work behind the scenes and simply passes the file around.
Ended up using RestTemplate instead as it actually constructs requests.

opendaylight : Storing a sting in MDSAL

I have a YANG model (known to MDSAL) which I am using in an opendaylight application. In my application, I am presented with a json formatted String which I want to store in the MDSAL database. I could use the builder of the object that I wish to store and set its with fields presented in the json formatted String one by one but this is laborious and error prone.
Alternatively I could post from within the application to the Northbound API which will eventually write to the MDSAL datastore.
Is there a simpler way to do this?
Thanks,
Assuming that your incoming JSON matches the structure of your YANG model exactly (does it?), I believe what you are really looking for is to transform that JSON into a "binding independant" (not setters of the generated Java class) internal model - NormalizedNode & Co. Somewhere in the controller or mdsal project there is a "codec" class that can do this.
You can either search for such code, and its usages (I find looking at tests are always useful) in the ODL controller and mdsal projects source code, or in other ODL projects which do similar things - I'm thinking specifically browsing around the jsonrpc and daexim projects sources; specifically this looks like it may inspire you: https://github.com/opendaylight/daexim/blob/stable/nitrogen/impl/src/main/java/org/opendaylight/daexim/impl/ImportTask.java
Best of luck.
Based on the information above, I constructed the following (which I am posting here to help others). I still do not know how to get rid of the deprecated reference to SchemaService (perhaps somebody can help).
private void importFromNormalizedNode(final DOMDataReadWriteTransaction rwTrx, final LogicalDatastoreType type,
final NormalizedNode<?, ?> data) throws TransactionCommitFailedException, ReadFailedException {
if (data instanceof NormalizedNodeContainer) {
#SuppressWarnings("unchecked")
YangInstanceIdentifier yid = YangInstanceIdentifier.create(data.getIdentifier());
rwTrx.put(type, yid, data);
} else {
throw new IllegalStateException("Root node is not instance of NormalizedNodeContainer");
}
}
private void importDatastore(String jsonData, QName qname) throws TransactionCommitFailedException, IOException,
ReadFailedException, SchemaSourceException, YangSyntaxErrorException {
// create StringBuffer object
LOG.info("jsonData = " + jsonData);
byte bytes[] = jsonData.getBytes();
InputStream is = new ByteArrayInputStream(bytes);
final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder = ImmutableContainerNodeBuilder.create()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(qname));
try (NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(builder)) {
SchemaPath schemaPath = SchemaPath.create(true, qname);
LOG.info("SchemaPath " + schemaPath);
SchemaNode parentNode = SchemaContextUtil.findNodeInSchemaContext(schemaService.getGlobalContext(),
schemaPath.getPathFromRoot());
LOG.info("parentNode " + parentNode);
try (JsonParserStream jsonParser = JsonParserStream.create(writer, schemaService.getGlobalContext(),
parentNode)) {
try (JsonReader reader = new JsonReader(new InputStreamReader(is))) {
reader.setLenient(true);
jsonParser.parse(reader);
DOMDataReadWriteTransaction rwTrx = domDataBroker.newReadWriteTransaction();
importFromNormalizedNode(rwTrx, LogicalDatastoreType.CONFIGURATION, builder.build());
}
}
}
}

Spring 3 RESTful return on POST (create)

I am new to RESTful services and their implementation on Spring 3. I would like your opinion on the best practices for returning type when a client creates a new resource in my server.
#RequestMapping(method = RequestMethod.POST,
value = "/organisation",
headers = "content-type=application/xml")
#ResponseStatus(HttpStatus.CREATED)
public ??? createOrganisation(#RequestBody String xml)
{
StreamSource source = new StreamSource(new StringReader(xml));
Organisation organisation = (Organisation) castorMarshaller.unmarshal(source);
// save
return ???;
}
A simple choice would be javax.ws.rs.core.Response, found in the Java EE's own restful services package. It - simply - tells what the web server should answer to the HTTP request.
For instance:
if (organisation != null)
return Response.ok().build();
else
return Response.serverError().build();
Custom response headers and other exotic things like that are possible with that return type too, but I don't think that would match with "best practices".
uh, I missed that #ResponseStatus(HttpStatus.CREATED)... I guess my answer was not much of help.
Maybe this will help instead: How to return generated ID in RESTful POST?
I would go for a ResponseEntity<byte[]> and you would have take care of the marshalling of your response on your controller method. Notice that you are basically scrapping the V in MVC, there is a MarshallingView on Spring but from experience I consider the previous solution much more flexible and easier to understand.
It is a good idea to return the newly created entity(with the generated id) wrapped in ResponseEntity. You can also set the HttpStatus in ResponseEntity based on the result of the operation.
#RequestMapping(method = RequestMethod.POST,
value = "/organization",
headers = "content-type=application/xml")
public ResponseEntity<Organization> createOrganisation(#RequestBody String xml) {
StreamSource source = new StreamSource(new StringReader(xml));
Organization organisation = (Organization) castorMarshaller.unmarshal(source);
// save
return new ResponseEntity<Organization>(organization, HttpStatus.OK);
}

Resources