How to use RemoteFileTemplate<SmbFile> in Spring integration? - spring

I've got a Spring #Component where a SmbSessionFactory is injected to create a RemoteFileTemplate<SmbFile>. When my application runs, this piece of code is called multiple times:
public void process(Message myMessage, String filename) {
StopWatch stopWatch = StopWatch.createStarted();
byte[] bytes = marshallMessage(myMessage);
String destination = smbConfig.getDir() + filename + ".xml";
if (log.isDebugEnabled()) {
log.debug("Result: {}", new String(bytes));
}
Optional<IOException> optionalEx =
remoteFileTemplate.execute(
session -> {
try (InputStream inputStream = new ByteArrayInputStream(bytes)) {
session.write(inputStream, destination);
} catch (IOException e1) {
return Optional.of(e1);
}
return Optional.empty();
});
log.info("processed Message in {}", stopWatch.formatTime());
optionalEx.ifPresent(
ioe -> {
throw new UncheckedIOException(ioe);
});
}
this works (i.e. the file is written) and all is fine. Except that I see warnings appearing in my log:
DEBUG my.package.MyClass Result: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>....
INFO org.springframework.integration.smb.session.SmbSessionFactory SMB share init: XXX
WARN jcifs.smb.SmbResourceLocatorImpl Path consumed out of range 15
WARN jcifs.smb.SmbTreeImpl Disconnected tree while still in use SmbTree[share=XXX,service=null,tid=1,inDfs=true,inDomainDfs=true,connectionState=3,usage=2]
INFO org.springframework.integration.smb.session.SmbSession Successfully wrote remote file [path\to\myfile.xml].
WARN jcifs.smb.SmbSessionImpl Logging off session while still in use SmbSession[credentials=XXX,targetHost=XXX,targetDomain=XXX,uid=0,connectionState=3,usage=1]:[SmbTree[share=XXX,service=null,tid=1,inDfs=false,inDomainDfs=false,connectionState=0,usage=1], SmbTree[share=XXX,service=null,tid=5,inDfs=false,inDomainDfs=false,connectionState=2,usage=0]]
jcifs.smb.SmbTransportImpl Disconnecting transport while still in use Transport746[XXX/999.999.999.999:445,state=5,signingEnforced=false,usage=1]: [SmbSession[credentials=XXX,targetHost=XXX,targetDomain=XXX,uid=0,connectionState=2,usage=1], SmbSession[credentials=XXX,targetHost=XXX,targetDomain=null,uid=0,connectionState=2,usage=0]]
INFO my.package.MyClass processed Message in 00:00:00.268
The process method is called from a Rest method, which does little else.
What am I doing wrong here?

Related

WebTestClient with multipart file upload

I'm building a microservice using Spring Boot + Webflux, and I have an endpoint that accepts a multipart file upload. Which is working fine when I test with curl and Postman
#PostMapping("/upload", consumes = [MULTIPART_FORM_DATA_VALUE])
fun uploadVideo(#RequestPart("video") filePart: Mono<FilePart>): Mono<UploadResult> {
log.info("Video upload request received")
return videoFilePart.flatMap { video ->
val fileName = video.filename()
log.info("Saving video to tmp directory: $fileName")
val file = temporaryFilePath(fileName).toFile()
video.transferTo(file)
.thenReturn(UploadResult(true))
.doOnError { error ->
log.error("Failed to save video to temporary directory", error)
}
.onErrorMap {
VideoUploadException("Failed to save video to temporary directory")
}
}
}
I'm now trying to test using WebTestClient:
#Test
fun shouldSuccessfullyUploadVideo() {
client.post()
.uri("/video/upload")
.contentType(MULTIPART_FORM_DATA)
.syncBody(generateBody())
.exchange()
.expectStatus()
.is2xxSuccessful
}
private fun generateBody(): MultiValueMap<String, HttpEntity<*>> {
val builder = MultipartBodyBuilder()
builder.part("video", ClassPathResource("/videos/sunset.mp4"))
return builder.build()
}
The endpoint is returning a 500 because I haven't created the temp directory location to write the files to. However the test is passing even though I'm checking for is2xxSuccessful if I debug into the assertion that is2xxSuccessful performs, I can see it's failing because of the 500, however I'm still getting a green test
Not sure what I am doing wrong here. The VideoUploadException that I map to simply extends ResponseStatusException
class VideoUploadException(reason: String) : ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, reason)

How do you use WebFlux to parse an event stream that does not conform to Server Sent Events?

I am trying to use WebClient to deal with the Docker /events endpoint. However, it does not conform to the text/eventstream contract in that each message is separated by 2 LFs. It just sends it as one JSON document followed by another.
It also sets the MIME type to application/json rather than text/eventstream.
What I am thinking of but not implemented yet is to create a node proxy that will add the required line feed and put that in between but I was hoping to avoid that kind of workaround.
Instead of trying to handle a ServerSentEvent, just receive it as a String. Then attempt to parse it as JSON (ignoring the ones that fail which I am presuming may happen but I haven't hit it myself)
#PostConstruct
public void setUpStreamer() {
final Map<String, List<String>> filters = new HashMap<>();
filters.put("type", Collections.singletonList("service"));
WebClient.create(daemonEndpoint)
.get()
.uri("/events?filters={filters}",
mapper.writeValueAsString(filters))
.retrieve()
.bodyToFlux(String.class)
.flatMap(Mono::justOrEmpty)
.map(s -> {
try {
return mapper.readValue(s, Map.class);
} catch (IOException e) {
log.warn("unable to parse {} as JSON", s);
return null;
}
})
.flatMap(Mono::justOrEmpty)
.subscribe(
event -> {
log.trace("event={}", event);
refreshRoutes();
},
throwable -> log.error("Error on event stream: {}", throwable.getMessage(), throwable),
() -> log.warn("event stream completed")
);
}

Log Spring webflux types - Mono and Flux

I am new to spring 5.
1) How I can log the method params which are Mono and flux type without blocking them?
2) How to map Models at API layer to Business object at service layer using Map-struct?
Edit 1:
I have this imperative code which I am trying to convert into a reactive code. It has compilation issue at the moment due to introduction of Mono in the argument.
public Mono<UserContactsBO> getUserContacts(Mono<LoginBO> loginBOMono)
{
LOGGER.info("Get contact info for login: {}, and client: {}", loginId, clientId);
if (StringUtils.isAllEmpty(loginId, clientId)) {
LOGGER.error(ErrorCodes.LOGIN_ID_CLIENT_ID_NULL.getDescription());
throw new ServiceValidationException(
ErrorCodes.LOGIN_ID_CLIENT_ID_NULL.getErrorCode(),
ErrorCodes.LOGIN_ID_CLIENT_ID_NULL.getDescription());
}
if (!loginId.equals(clientId)) {
if (authorizationFeignClient.validateManagerClientAccess(new LoginDTO(loginId, clientId))) {
loginId = clientId;
} else {
LOGGER.error(ErrorCodes.LOGIN_ID_VALIDATION_ERROR.getDescription());
throw new AuthorizationException(
ErrorCodes.LOGIN_ID_VALIDATION_ERROR.getErrorCode(),
ErrorCodes.LOGIN_ID_VALIDATION_ERROR.getDescription());
}
}
UserContactDetailEntity userContactDetail = userContactRepository.findByLoginId(loginId);
LOGGER.debug("contact info returned from DB{}", userContactDetail);
//mapstruct to map entity to BO
return contactMapper.userEntityToUserContactBo(userContactDetail);
}
You can try like this.
If you want to add logs you may use .map and add logs there. if filters are not passed it will return empty you can get it with swichifempty
loginBOMono.filter(loginBO -> !StringUtils.isAllEmpty(loginId, clientId))
.filter(loginBOMono1 -> loginBOMono.loginId.equals(clientId))
.filter(loginBOMono1 -> authorizationFeignClient.validateManagerClientAccess(new LoginDTO(loginId, clientId)))
.map(loginBOMono1 -> {
loginBOMono1.loginId = clientId;
return loginBOMono1;
})
.flatMap(o -> {
return userContactRepository.findByLoginId(o.loginId);
})

SerializationException in Parse cloud code. mdhost.exe crashes

Setting up an nunit test project for some parse cloud code. Testing the hello world example cloud function I receive the following unhandled exception in Xamarin studio. (Test has passed a few times, but still receive exception)
SerializationException: Type 'Parse.ParseException' in Assembly 'Parse, Version=1.5.4.0, Culture=neutral, PublicKeyToken=ba48c3a442de616e' is not marked as serializable.
Also, mdhost.exe crashes a few seconds after the test runs.
Here is the Nunit test code.
[Test()]
public async void HelloTest(){
IDictionary<string, object> param = new Dictionary<string, object> ();
await ParseCloud.CallFunctionAsync<string>("hello",param).ContinueWith(t => {
string resp = t.Result;
Assert.AreEqual("Hello world!", resp);
});
}
I'm running Xamarin Studio 5.9.5 (build 9) on win7
Copied code over to Xamarin 5.9.4 (build 5) on mac. Test runs and passes without getting mdhost.exe crash. However still showing SerializationException.
(Misread your question on the first answer)
Your Parse call is failing with a Parse.ParseException and that exception is what is trying to be serialized/deserialized. Wrapping a try/catch and Asserting the exception should work in order to show the error code what is actually failing in the test. (I can not get a real Parse ParseException to throw but this should get you started).
Than you can lookup the code:
[Test ()]
public async void HelloTest ()
{
IDictionary<string, object> param = new Dictionary<string, object> ();
try {
await ParseCloud.CallFunctionAsync<string> ("hello", param).ContinueWith (t => {
string resp = t.Result;
Assert.AreEqual ("Hello world!", resp);
});
} catch (ParseException ex) {
Assert.Fail (String.Format("{0} : {1}", ex.Code, ex.Message));
} catch (Exception ex) {
Assert.Fail (ex.Message);
}
}

Error processing GroovyPageView: getOutputStream() has already been called for this response

I have an action that writes directly to the output stream. Sometimes I get the following to errors:
Error processing GroovyPageView: getOutputStream() has already been called for this response
Caused by getOutputStream() has already been called for this response
and this one:
Executing action [getImage] of controller [buddyis.ItemController] caused exception: Runtime error executing action
Caused by Broken pipe
How can I solve these problems? The action I use is listed below.
NOTE: I use Tomcat 7.0.42, if this is important!
def getImage() {
byte [] imageByteArray = // some image bytes
response.setHeader 'Content-disposition', "attachment; filename=\"${imageName}${imageExtension}\""
response.setContentType("image/pjpeg; charset=UTF-8")
response.contentLength = imageByteArray.size()
response.outputStream.write(imageByteArray)
response.outputStream.flush()
response.outputStream.close()
return
}
I don't know why you are getting that error, however here is what I do that works everytime.
I don't call .flush() or .close()
response.setContentType("application/octet-stream")
response.setHeader("Content-disposition", "filename=\"${name}\"")
response.setContentLength(imageByteArray.size())
response.outputStream << imageByteArray
Using the above it was working fine, until I found out a user can cancel a download, which caused an exception. This is the full code I use instead of response.outputStream << imageByteArray:
def outputStream = null
try {
outputStream = response.outputStream
outputStream << imageByteArray
} catch (IOException e){
log.debug('Canceled download?', e)
} finally {
if (outputStream != null){
try {
outputStream.close()
} catch (IOException e) {
log.debug('Exception on close', e)
}
}
}
I had this issue whilst running grails 2.5 on tomcat:7.0.55.3 and with the java-melody grails plugin installed. After uninstalling java-melody it worked just fine

Resources