Fail a file download in the middle of process in Spring MVC - java

I have an endpoint in Spring MVC controller that allow user to start download a file while I still process necessary data on the server. The endpoint looks like this:
#GetMapping(value = "/download.csv")
public void download(HttpServletResponse response) {
try {
response.setContentType(APPLICATION_OCTET_STREAM);
response.addHeader("Content-Disposition", "attachment; filename=name.csv");
final List listOfIds = getListOfIds();
for (id in ids) {
final String data = getAdditionalData(id)
write(data, response.getOutputStream())
response.flushBuffer()
}
} catch (Exception ex) {
// handle error
}
}
This code works fine, when the user accesses the download endpoint, the web browser will immediately ask if the user wants to save the file. Then I can process the expensive getAdditionalData(id) calls on the server during user downloading, instead of letting the user wait until I processed all the ids which could take a long time.
However, there is one problem: when getAdditionalData(id) fails and throws an exception, the web browser will show the download as "completed" instead of "failed" and leave a partial result to the user.
My problem is: is there any way from this endpoint to tell the web browser the download has failed so it won't show "completed" to the user? Thanks.

Related

Vert.x Web (Scala): Read Multipart File Upload as Stream from Router without Saving to Disk

I know that it is possible to handle file uploads through a Vert.x Web Router simply using something like so:
myRouter.post("/upload")
.handler(BodyHandler.create("myUploadDirectory")))
.handler { context =>
// do something with the uploaded files and respond to the request
}
However, this saves the file on the local server (or maybe even a network share). It might be perfectly fine to buffer small files on disk temporarily and move them to another store in batches, but the same cannot be said for very large files (multiple gigabytes, for example).
What is a good way to read the file upload as a stream of bytes, for example, and write it directly to a final store, and then be able to handle failures and successes gracefully, all from a Router?
Proxying the upload this would avoid making the store publicly accessible to clients and possibly allow more fine-grained control of the upload process than just creating a local file on the server or exposing the object/blob store.
EDIT:
I know that an alternative is to to do something like this, to handle the file upload as a special case before handling requests with my Router:
val myHttpServer = myVertx.createHttpServer()
myHttpServer.requestHandler(request => {
if(request.absoluteURI().contains("/upload")) {
request.setExpectMultipart(true)
request.handler { buffer =>
// upload part of the file
}
request.endHandler { end =>
// perform some action when the upload is done
}
} else
myRouter.handle(request)
})
However, as you can see it looks pretty messy. It would be much cleaner to handle it with a Router.post() or something similar.
Am I going about this wrong or something?
I've tried doing the following to no avail (I only get an HTTP 500 and no useful errors in the log). Not even the exceptionHandler is fired.
myRouter.post("/upload")
.handler { context =>
context.request.setExpectMultipart(true)
context.request.uploadHandler { upload =>
upload.handler { buffer =>
// send chunk to backend
}
upload.endHandler { _ =>
// upload successful
}
upload.exceptionHandler { e =>
// handle the exception
}
}
}
SOLUTION:
So it turns out that I had added a BodyHandler to the Router before adding my routes. This is because I wanted to be able to receive a JSON body in other POST requests, and didn't want to have to type .handler(BodyHandler.create()) before every route that received JSON.
However, as stated in the name of the class... the body was then handled, meaning that I would be unable to add the UploadHandler to the request.
I'm not knowledgeable with Scala but here's a solution for Java:
router.post("/upload").handler(rc -> {
HttpServerRequest req = rc.request();
req.setExpectMultipart(true);
req.uploadHandler(upload -> {
upload.exceptionHandler(cause -> {
req.response().setChunked(true).end("Upload failed");
});
upload.endHandler(v -> {
req.response().setChunked(true).end("Successfully uploaded file");
});
upload.handler(buffer -> {
// Send to backend
});
});
});

How can I use Spread Sheets API as a batch program?

I made a batch program which get some spread sheets and update them with Client Login method.
It had moved until 26 May.
But, it is no longer work now because authentication error happen.
I want to execute a batch program for SpreadSheets using the cron.
So, I tried to read the below pages and to migrate to OAuth2.0 method.
https://developers.google.com/api-client-library/java/google-api-java-client/client-login
https://developers.google.com/google-apps/spreadsheets/authorize
However, I couldn't understand how I use OAuth2.0 as a batch in Java code.
What I want to know is an authentication way that it doesn't need to operate manually when Spread Sheets API is run.
Please tell me a solution of this problem.
Thank you.
// current java source code
public static SpreadsheetService getService() {
SpreadsheetService service = new SpreadsheetService("MySpreadsheetIntegration");
service.setProtocolVersion(SpreadsheetService.Versions.V3);
// get Username and Password from JSON file
String username = getValueJsonFile("Username", JSON_FILE);
String password = getValueJsonFile("Password", JSON_FILE);
try {
service.setUserCredentials(username, password);
} catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("Error code: " + e.getCodeName());
log.error(getMessage("WRONG_USER_PASS"));
}
return service;
}

How to handle urn:ietf:wg:oauth:2.0:oob redirect in Google Calendar API Authorization

I am currently working on a installed desktop application implemented in java. I intend to integrate Google Calendar API into the application.
During the authorization procedure, I come to this stage where I am able to get the authorization code only through triggering a browser where the user consent page is displayed. Users then have to click "accept" and will be redirected to a webpage where the authorization code is presented. Users are to copy this code to the Eclipse System.in in order for the authorization process to continue (to exchange the authorization code for a TokenResponse).
My question is that how can I simplify this process so that the user won't have to do this stupid copy-and-paste stuff for the authorization code to be received? (This won't work anyway, if the project is compiled into a jar file...) Currently all I know is that I will need to provide a callbackurl or something, I just can't figure this out. Therefore, I would appreciate a more concrete answer, rather than simply tell me the concepts.
Thanks in advance.
You have to use a service account (which comes with a private key) in order to skip the step involving user interaction. There is a detailed guide about this here.
The oauth2 authorization grant flow (I think, that's what you are doing) defines that your application gets the flow back via a HTTP redirect.
It's like this:
Your application opens a socket and listens there for HTTP requests
It now opens the browser and lets the user enter his/her credentials
The user clicks submit and sends the credentials to the oauth server
The server checks the credentials and, if correct, redirects the browser to your application (to the socket you opened in 1.)
Your application gets the auth code from the browser and exchanges it with the access ticket.
To let the server know where to redirect to, you use the oauth parameter redirect_uri in step 2.
This page seems to indicate that the auth code is in the title of the browser window, and the desktop app is expected to read it from there. (Gack.)
I found the solution.
Note: this is java code, but I bet it works the same way in all other languages.
The problem is my server is very restricted with and so I cannot start either browser there(since that is just a server without UI), either start localhost server for getting the code.
All you need is custom VerificationCodeReceiver:
VerificationCodeReceiver inbrowserListener = new VerificationCodeReceiver() {
#Override
public String getRedirectUri() throws IOException {
return "urn:ietf:wg:oauth:2.0:oob";
}
#Override
public String waitForCode() throws IOException {
// Reading console line
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
return reader.readLine();
}
#Override
public void stop() throws IOException {
}
};
then simply use it in usual flow:
private static Credential getCredentials() {
.....
return new AuthorizationCodeInstalledApp(flow, inbrowserListener).authorize("user");
}

Java - refresh opened html page from application

I have an application that creates a html page from app (I use freemarker). After that, I open the generated webpage from application using Desktop like this:
public void openPage() {
if (Desktop.isDesktopSupported()) {
try {
File file = new File("index.html");
Desktop.getDesktop().open(file);
} catch (IOException ex) {
System.out.println("Error opening a html page.");
ex.printStackTrace();
}
}
}
Now, my question is: Is there a way to refresh the page from my application? I am changing the concent dynamically and I would like to refresh the page in the browser every few seconds.
Or would it be better to just update the page on background and refresh it directly in the html code using javascript?
Thanks for any tips!
EDIT: Note, that I would like to communicate back to my java application from some form on that webpage (for example sending parametres to specify the way my page is updated)
Use AJAX technology (jQuery pretty much fits your needs) to invoke a server side controller in your application. You can then negotiate the need for a data update. A JSON API is recommended for this. You can use Jackson for JSON-related operations in your Java code.
To save bandwidth, you could poll for only a boolean value to determine whether the server has new data since your last update (e.g. provide since=[some_timestamp] as request param) and query for the actual data only if it makes sense (that is, the server returned true).

How to communicate a "warning" message from the Service/DAO to the UI?

I have a Java web application. It's basically something like this:
editUser.jsp -> ControllerServlet.java -> UpdateUserCommand.java -> UserService.java -> UserDAO.java -> USER_TABLE.db
The user clicks a 'Save Changes' button on the editUser.jsp page, on success, the view is redirected to the viewUser.jsp page where they are presented with a read only view of the user and a "User changes successfully saved" message is displayed.
For example, UpdateUserCommand.java does:
user.setWhatevers( /* whatevers */ );
try {
user = this.userService.updateUser(user);
messages.recordSuccessMessageForView("User changes successfully saved.");
} catch (Exception x) {
messages.recordErrorMessageForView("There was an error; "+x.getMessage());
}
/* redirect to viewUser.jsp page */
My issue is, inside UserService.java I call something else -- let's say a NotificationService.java that sends an email. If the send email fails, I log the email sending error, but I proceed normally.
The result is, the user sees the "Successully saved!" message, but there's no visible indication that the email wasn't sent. (Even though the send email error is logged on the server.)
For example, UserService.java does:
public User updateUser(User user) throws Exception {
try {
this.userDAO.updateUser(user);
try {
this.notificationService.sendEmail("bob#bob.com", "user saved");
} catch (Exception x) {
logger.log("Error sending email; "+x.getMessage();
}
} catch (Exception x) {
throw new Exception("Error saving!", x);
}
}
So my question is, how would you suggest I communicate from the UserService back to the UpdateUserCommand that there is an email-related warning message to display in the UI?
UpdateUserCommand.java can easily do a:
messages.recordWarning("Could not send the email.");
But the UserService.java doesn't have access to the 'messages' object. And I don't want the UserService to throw an exception because I want everything to proceed as if everything is normal.
Any suggestions are greatly appreciated!
Rob
Two possible solutions:
Have your Service method return some type that encapsulates both the User that it current responds and something representing notification-type messages. This solution is nicer than #2 as it's a bit easier to test.
Have noticiationService or some other service record it's "messages for the web user" in a ThreadLocal which you then access and clear on the UI layer. This option is a little bit uglier in that you are essentially keeping global data around (and you are assuming that one thread = one request), but it doesn't require changes to the signatures of existing methods.

Categories