I'm trying to set up automatic cleanup of temp files, but some strange things happen when a Mono created from a CompletableFuture (from the S3 sdk) comes into play. The temp file is deleted too early in the chain. I am wondering whether this is a misunderstanding of reactor from my side, or a bug.
I've condensed the code to mimic the behavior I'm experiencing.
I would expect the output of this test to be:
uploaded file response
do other stuff
clean up file
However it is:
clean up file
uploaded file response
do other stuff
When not creating a Mono using Mono.fromFuture, everything works as expected.
#Test
void bla() throws InterruptedException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
// "uploading"
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "uploaded file response";
});
Mono<String> createTmpFile = Mono.just("create tempFile")
.doAfterTerminate(() -> System.out.println("clean up file"));
Mono<String> doPersistenceThings = Mono.fromFuture(future)
.doOnNext(System.out::println)
.then(Mono.just("do other stuff"));
Mono.just("data")
.flatMap(d -> createTmpFile)
.flatMap(d -> doPersistenceThings)
.subscribe(System.out::println);
Thread.sleep(2000);
}
Related
Inspired by .Net TPL, I'm trying to find a way to handle an error outside the RX pipe. Specifically, on error, I wish the Observer pipe to stop, and pass control back to the surrounding method. Something like:
public void testRxJava() {
try {
Observable.range(0, 5)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(i -> { throw new RuntimeException(); })
.subscribe();
} catch (Exception ex) {
// I was hoping to get here on the main thread, but crashed instead
Log.i("Test", "Will never get here");
}
}
This will cause the application to crash with an io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException, which will not be caught in the catch clause, and will instead invoke the main thread's uncaughtException() handler.
Trying to throw from the error handler in subscribe() does not work either, again falling back to the uncaughtException() handler.
Is there a way to re-throw or otherwise pass the error information to the calling method?
A similar question for C# is found here.
Have you tried catching the error like so
Observable.range(0, 5)
.subscribeOn(Schedulers.newThread())
.doOnError {
//your error caught here
}
.observeOn(AndroidSchedulers.mainThread())
.map({ i -> throw RuntimeException() })
.subscribe()
Here's what I ended up doing. As far as I can tell, this is the only was to leave the ReactiveX pipe, and let the surrounding code handle the error. Would be happy if someone has a more elegant way:
public void testRxJava() {
try {
// will be null if no error, will hold a Throwable on error
AtomicReference<Throwable> opError = new AtomicReference<>(null);
Observable.range(0, 5)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(i -> { throw new RuntimeException(); }) // throws
.blockingSubscribe(
result -> Log.i(TAG, "will never happen"),
error -> { opError.set(error); } // sets reference to the error without crashing the app
);
// re-throw
if(opError.get() != null) {
throw new Exception(opError.get());
}
} catch (Exception ex) {
Log.e("Test", "exception", ex);
}
}
I am implementing tests for my Vert.x application, but I am having issues in making Vert.x wait for the deploy of the Verticle in a graceful way.
This is my #BeforeClass Method:
#BeforeClass
public static void before(TestContext context)
{
vertx = Vertx.vertx();
DeploymentOptions options = new DeploymentOptions();
byte[] encoded;
JsonObject config;
try {
encoded = Files.readAllBytes(Paths.get("src/main/resources/config.json"));
config = new JsonObject(new String(encoded, Charset.defaultCharset()));
options.setConfig(config);
jdbc = JDBCClient.createShared(vertx, config , "PostgreSQL");
deployVerticle((result) -> loadTestData((result), jdbc), options);
while (true)
{
if (vertx.deploymentIDs().size() > 0)
break;
}
} catch
(IOException e)
{
e.printStackTrace();
}
}
Also, here is the implementation for the deployVerticle and loadTestData methods:
private static void deployVerticle(Handler<AsyncResult<Void>> next, DeploymentOptions options) {
vertx.deployVerticle(PostgreSQLClientVerticle.class.getName(), options, deployResult ->
{
if (deployResult.succeeded())
next.handle(Future.succeededFuture());
});
}
private static void loadTestData(AsyncResult<Void> previousOperation, JDBCClient jdbc)
{
if (previousOperation.succeeded())
{
jdbc.getConnection(connection -> {
if (connection.succeeded())
{
connection.result().query(deleteTestDataGeneration, queryResult ->
{
connection.result().close();
});
}
});
}
}
As you can see, right now I have a while (true) on the beforemethod to hold the process and make sure the verticle is actually deployed.
Otherwise, when the tests start running, the verticle is not yet fully deployed and I get a NullPointerException trying to reach the resources.
I've tried many different approaches like using CompositeFuture or using Future.compose method to make the "before tasks" sequential and make the program hold for completion.
I achieved in making those tasks sequential but failed on holding the process until they are completed.
One of the issues is, I think, the fact that the deployVerticle method returns the AsyncResult with succeeded == true after every step of the "deploy procedure" is done, instead of when the Verticle is totally up.
Meaning that the process gets a successful result before everything is actually up...but this is just a wild guess.
Bottom-line: I would like to find a way to wait for the Verticle to be totally deployed before proceeding to perform the tests, without having to do the while (true)loop that I currently have in there.
What you are missing is the Async async = context.async();. With that the unittest stays in the method until it is not set to complete. Then you are able to orchestrate your asychronous code to:
first deploy the verticle
then execute the loadtestGeneration
set the async to complete so that, the other unittest methods already can access to your verticle without nullpointerexception
I also made some cleanup, check it out:
BeforeClass
#BeforeClass
public static void before2(TestContext context){
Async async = context.async();
vertx = Vertx.vertx();
DeploymentOptions options = new DeploymentOptions();
byte[] encoded;
JsonObject config;
try {
encoded = Files.readAllBytes(Paths.get("src/main/resources/config.json"));
config = new JsonObject(new String(encoded, Charset.defaultCharset()));
options.setConfig(config);
jdbc = JDBCClient.createShared(vertx, config , "PostgreSQL");
deployVerticle2(options)
.compose(c -> loadTestData2(jdbc))
.setHandler(h -> {
if(h.succeeded()){
async.complete();
}else{
context.fail(h.cause());
}
});
} catch (IOException e){
context.fail(e);
}
}
DeployVerticle
private static Future<Void> deployVerticle2(DeploymentOptions options) {
Future<Void> future = Future.future();
vertx.deployVerticle(PostgreSQLClientVerticle.class.getName(), options, deployResult -> {
if (deployResult.failed()){
future.fail(deployResult.cause());
}else {
future.complete();
}
});
return future;
}
LoadTestData
private static Future<Void> loadTestData2(JDBCClient jdbc){
Future<Void> future = Future.future();
jdbc.getConnection(connection -> {
if (connection.succeeded()) {
connection.result().query(deleteTestDataGeneration, queryResult -> {
if(queryResult.failed()){
connection.result().close();
future.fail(queryResult.cause());
}else{
connection.result().close();
future.complete();
}
});
} else {
future.fail(connection.cause());
}
});
return future;
}
How to move a File in a FTP route to a different directory based on the error?
from("sftp://XXX#safsdf.de/dir/?delay=2s&move=done&moveFailed=failImport")
.split()
.body()
.process(e -> {
String fileName = (String) e.getIn().getHeader(Exchange.FILE_NAME);
// do some magic which could throw a exception
})
.log("Imported file ${file:name} completely.");
Check out the onException handler in Camel. It allows you to catch specific exceptions and then route accordingly. There is also try-catch-like syntax if you want the error handling to be more in-lined with your Camel route.
onException(BadThingHappenedException.class).to(file:///errorFolder)
Documentation:
onException
try-catch-finally
Custom file processing strategy
It my be archived with a onException, try-catch-finally or Custom file processing strategy but none of it is really handy.
I came up with this simple code:
#Override
public void configure() throws Exception {
errorHandler(loggingErrorHandler(logger));
from("sftp://XXX#safsdf.de/dir/?delay=2s&move=${in.header.outDirectory}&moveFailed=failImport")
.process(exchange -> exchange.setProperty(originalMessage, exchange.getIn()))
.split()
.body()
.process(e -> {
String fileName = (String) e.getIn().getHeader(Exchange.FILE_NAME);
Message origMsg = (Message) e.getProperty(originalMessage);
try {
// do what ever
origMsg.setHeader(outDirectory, "done/" + fileName);
} catch (KaboomException ex) {
origMsg.setHeader(outDirectory, "retry/" + fileName);
}
})
.log("Imported file ${file:name} completely.");
}
Its important to set the header on the original message.
I have to make calls to long running methods in a Spark API implementation. These methods return CompletableFutures, so I'd like to free up the current Thread by triggering Spark to answer the client request in a callback.
As far as I can tell this is not possible with Spark, but I'd like to make sure I am not overlooking anything.
To illustrate the question, see the small code sample below.
import spark.Spark;
import java.util.concurrent.CompletableFuture;
public class HelloSpark {
public static void main(String[] args) {
Spark.get("/what_i_have_to_do", (req, res) -> {
CompletableFuture<String> f = callToSlowWorker();
return f.get();
});
Spark.get("/what_i_would_like_to_do", (req, res) -> {
CompletableFuture<String> f = callToSlowWorker();
f.whenComplete((answer, throwable) -> {
if(throwable != null){
// send error to requesting client
res.status(500);
res.body(throwable.getMessage());
} else {
// send answer to requesting client
res.body(answer);
}
// trigger spark to return the response to the client
// ...?!?
});
return null; // obviously not what I want, just to make this sample code compile
});
}
static CompletableFuture<String> callToSlowWorker(){
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
return "Hello World";
});
}
}
SparkJava is currently blocking only, thus what you've described is not possible. There is an open enhancement request to add support for a non blocking API.
I'm sending files to my local server that creates a file back. My problem occurs when the user perform multiple actions one after another and I need to show an error message if one of the requests don't get a feedback file in 5 min.
How can I handle all these requests? I used newSingleThreadScheduledExecutor to check if the feedback file is there every minute but I don't know how to handle multiple ones and keep the countdown to each request for the 5 min case.
My try:
ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(listPrinter.size()));
for(int i=0;i<list.size();i++){
try {
final File retrievedFile = new File("/home/"+list.get(i)+".csv");
ListenableFuture<File> future = executor.submit(new Callable<File>() {
public File call() {
// Actually send the file to your local server
// and retrieve a file back
if(retrievedFile.exists())
{
new Notification("file exits").show(Page.getCurrent());
}
else{
new Notification("file no exits").show(Page.getCurrent());
}
return retrievedFile;
}
});
future.get(5, TimeUnit.MINUTES);
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
} catch (ExecutionException ex) {
Exceptions.printStackTrace(ex);
} catch (TimeoutException ex) {
Exceptions.printStackTrace(ex);
new Notification("Time out").show(Page.getCurrent());
}
}
But it just get executed at the beginning and that's it but when the file is added nothing happens.
Is it possible to do this with watchService? It works pretty well for me but I didn't know about the 5 min case
Take a look to the Future interface:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html
should fit perfectly to your problem.
When you run a thread, the result could be a Future, it is the result of a asyncronous task, and you can have one Future per asyncronous task that you are launching.
Future<File> sendReceiveFile(File inputFile) {
final Future<File> future = new YourFuture<File>(...);
new Thread() {
#Override
public void run() {
File outputFile = null;
try {
outputFile = SendFileToServer(inputFile);
} catch (final Exception e) {
// do something
} finally {
future.setValue(fileOutput);
}
}
}.start();
return future;
}
And in your main:
Future<File> future = sendReceiveFile(myFile);
File outputFile = null;
try {
outputFile = future.get(1, TimeUnit.MINUTE);
} catch(TimeOutException e) {
// do something
}
You could do this manually, but using Guava ListenableFuture would be much better:
// Here we create a fixed thread pool with 10 threads and an inifinite-capacity queue
ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
final File fileToSend = ...; //
ListenableFuture<File> future = executor.submit(new Callable<File>() {
public File call() {
// Actually send the file to your local server
// and retrieve a file back
File retrievedFile = YourLocalServer.sendAndRetrieve(fileToSend);
return retrievedFile;
}
});
Futures.addCallback(future, new FutureCallback<File>() {
public void onSuccess(File retrievedFile) {
// Handle the successfully retrieved file when it returns
}
public void onFailure(Throwable thrown) {
// Handle the error
}
});
By sending the file asynchronously, you can send and retrieve many files at any given time. Then, when the server responds (either with a retrieved file or with an error), you can handle the response (retrieved file or exception) just when it comes back, without needing to wait for it. This means that the onSuccess() or onFailure() methods will be automatically executed when there's a response available from your local server.
I solved the problem by using a Timer that is executed every 5 minutes getting all the db transactions that happened for the last 5 minutes and didn't get any response and show my error code. It works pretty good. Thanks everyone for the help