How to detect when Vaadin FileDownloader succeeds or fails - vaadin

I have Vaadin 7 code to give the user an option to download a file:
Button btnDownloadResults = new Button("Download Results", FontAwesome.CLOUD_DOWNLOAD);
resource = new StreamResource(new MyStreamResource(), suggestedSaveAsFilename);
new FileDownloader(resource).extend(btnDownloadResults);
I would like to trigger code when the download has succeeded, or even if the download manages to start. Uses for this include closing a window, starting a progress spinner, or incrementing a download count.
Unlike the Vaadin Upload component, the FileDownloader does not have any listeners for finding out when a file download fails, succeeds, or starts.
Here is a simplified version of my StreamResouce subclass:
public class MyStreamResource implements StreamSource {
#Override
public InputStream getStream() {
String filename = /* code to determine the filename */;
try {
final File results = new File(FilenameUtils.normalize(filename));
return new FileInputStream(results);
} catch (FileNotFoundException fnfe) {
String errorMsg = "Cannot download results. Try again later, or contact your sysadmin.";
Utilities.showError(errorMsg);
return null;
} catch (Exception e) {
Utilities.logAndShowException(e);
return null;
}
}
}
Note that the getStream method returns before the user has even been prompted where to save the file (which they can choose to cancel.) So I can't trigger anything from inside that method.
One suggestion I got was to subclass the FileDownloader as follows:
FileDownloader fileDownloader = new FileDownloader(fileInputStream) {
private static final long serialVersionUID = -4584979099145066535L;
#Override
public boolean handleConnectorRequest(VaadinRequest request, VaadinResponse response, String path) throws IOException {
boolean result = super.handleConnectorRequest(request, response, path);
if (result) {
/* YOUR LOGIC GOES HERE */
}
return result;
}
} ;
Again, this fires too soon (and the boolean result is always true, even if my StreamSource returns null.)
Any suggestions?

After more research I believe the answer is that there is no simple way to get this information from the FileDownloader.
The difficulty appears to be a consequence of the way the FileDownloader is designed. From the FileDownloader docs:
"Download should be started directly when the user clicks e.g. a Button without going through a server-side click listener to avoid triggering security warnings in some browsers."
Because there is no round-trip back to the web server, there is no place to respond when the download fails, starts, or succeeds.
Some vague (and possibly bad) ideas for a workaround:
Have JS post some kind of asynchronous notification to the web
server, letting it know what happened. (Using JMS or Ajax?)
If there was some kind active process on the backend involved with transferring the file, it
would know when the transfer happened.
But the short answer seems to be there is no built-in way in Vaadin to do it.

Related

MockWebServer: llegalStateException: start() already called

I try to run a test with MockWebServer.
I would like to make a UI test with mocked response, so I could test for valid\invalid UI changes like logging in, or showing error in a login API.
However, each and every time I ran the code I got the following exception:
java.lang.IllegalStateException: start() already called
Code:
#RunWith(AndroidJUnit4.class)
public class UITestPlayground {
String testUrl = "http://testurl.com/";
MockWebServer server = new MockWebServer();
#Rule
public IntentsTestRule<LoginActivity> mIntentsRule = new IntentsTestRule<>(LoginActivity.class);
#Before
public void beforeHelper() throws IOException {
TestHelper.removeUserAndTokenIfAny(mIntentsRule.getActivity());
URLS.baseUrl = testUrl;
server.url(URLS.baseUrl);
//try to shutting down the server JUT IN CASE...
server.shutdown();
server.start();
}
#After
public void afterHelper() throws IOException {
server.shutdown();
}
#Test
public void invalidLoginDueNotValidJSONResponse() {
server.enqueue(new MockResponse().setBody("Something not valid JSON response"));
String emailToBeTyped = "tester#tester.com";
String passToBeTyped = "passtest";
ViewActions.closeSoftKeyboard();
// Type text and then press the button.
onView(withId(R.id.login_email_edit)).perform(typeText(emailToBeTyped));
ViewActions.closeSoftKeyboard();
onView(withId(R.id.login_pass_edit)).perform(typeText(passToBeTyped));
ViewActions.closeSoftKeyboard();
onView(withId(R.id.log_in_btn)).perform(click());
//TODO: check on valid error message appearing
}
}
What am I doing wrong? The .start() only called once, I even .shutdown() just in
case... I don't understand how could it called more than once.
Thanks in advance.
In the original example at github I have found that the order is reversed.
You actually start the server, THEN sets it's url.
And not setting the url then starting the server.
Interesting.

How do I get the current attempt number on a background job in Hangfire?

There are some database operations I need to execute before the end of the final attempt of my Hangfire background job (I need to delete the database record related to the job)
My current job is set with the following attribute:
[AutomaticRetry(Attempts = 5, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
With that in mind, I need to determine what the current attempt number is, but am struggling to find any documentation in that regard from a Google search or Hangfire.io documentation.
Simply add PerformContext to your job method; you'll also be able to access your JobId from this object. For attempt number, this still relies on magic strings, but it's a little less flaky than the current/only answer:
public void SendEmail(PerformContext context, string emailAddress)
{
string jobId = context.BackgroundJob.Id;
int retryCount = context.GetJobParameter<int>("RetryCount");
// send an email
}
(NB! This is a solution to the OP's problem. It does not answer the question "How to get the current attempt number". If that is what you want, see the accepted answer for instance)
Use a job filter and the OnStateApplied callback:
public class CleanupAfterFailureFilter : JobFilterAttribute, IServerFilter, IApplyStateFilter
{
public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
try
{
var failedState = context.NewState as FailedState;
if (failedState != null)
{
// Job has finally failed (retry attempts exceeded)
// *** DO YOUR CLEANUP HERE ***
}
}
catch (Exception)
{
// Unhandled exceptions can cause an endless loop.
// Therefore, catch and ignore them all.
// See notes below.
}
}
public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
// Must be implemented, but can be empty.
}
}
Add the filter directly to the job function:
[CleanupAfterFailureFilter]
public static void MyJob()
or add it globally:
GlobalJobFilters.Filters.Add(new CleanupAfterFailureFilter ());
or like this:
var options = new BackgroundJobServerOptions
{
FilterProvider = new JobFilterCollection { new CleanupAfterFailureFilter () };
};
app.UseHangfireServer(options, storage);
Or see http://docs.hangfire.io/en/latest/extensibility/using-job-filters.html for more information about job filters.
NOTE: This is based on the accepted answer: https://stackoverflow.com/a/38387512/2279059
The difference is that OnStateApplied is used instead of OnStateElection, so the filter callback is invoked only after the maximum number of retries. A downside to this method is that the state transition to "failed" cannot be interrupted, but this is not needed in this case and in most scenarios where you just want to do some cleanup after a job has failed.
NOTE: Empty catch handlers are bad, because they can hide bugs and make them hard to debug in production. It is necessary here, so the callback doesn't get called repeatedly forever. You may want to log exceptions for debugging purposes. It is also advisable to reduce the risk of exceptions in a job filter. One possibility is, instead of doing the cleanup work in-place, to schedule a new background job which runs if the original job failed. Be careful to not apply the filter CleanupAfterFailureFilter to it, though. Don't register it globally, or add some extra logic to it...
You can use OnPerforming or OnPerformed method of IServerFilter if you want to check the attempts or if you want you can just wait on OnStateElection of IElectStateFilter. I don't know exactly what requirement you have so it's up to you. Here's the code you want :)
public class JobStateFilter : JobFilterAttribute, IElectStateFilter, IServerFilter
{
public void OnStateElection(ElectStateContext context)
{
// all failed job after retry attempts comes here
var failedState = context.CandidateState as FailedState;
if (failedState == null) return;
}
public void OnPerforming(PerformingContext filterContext)
{
// do nothing
}
public void OnPerformed(PerformedContext filterContext)
{
// you have an option to move all code here on OnPerforming if you want.
var api = JobStorage.Current.GetMonitoringApi();
var job = api.JobDetails(filterContext.BackgroundJob.Id);
foreach(var history in job.History)
{
// check reason property and you will find a string with
// Retry attempt 3 of 3: The method or operation is not implemented.
}
}
}
How to add your filter
GlobalJobFilters.Filters.Add(new JobStateFilter());
----- or
var options = new BackgroundJobServerOptions
{
FilterProvider = new JobFilterCollection { new JobStateFilter() };
};
app.UseHangfireServer(options, storage);
Sample output :

CodenameOne Connection Request hangs when repeated

I have the following Codename One code for accessing a network resource. It is almost an exact copy of the Codename One tutorial for this use case.
public void executeRequest(){
String url = "http://www.random.net";
InfiniteProgress prog = new InfiniteProgress();
final Dialog dlg = prog.showInifiniteBlocking();
ConnectionRequest r = new ConnectionRequest() {
#Override
protected void postResponse() {
//handle changes to my form
}
#Override
protected void readResponse(InputStream input)
throws IOException {
//handle parsing data
}
#Override
protected void handleIOException(IOException err) {
super.handleIOException(err);
}
};
r.setUrl(url);
r.setPost(false);
r.addArgument("arg", "2");
r.setDuplicateSupported(true);
r.setDisposeOnCompletion(dlg);
NetworkManager.getInstance().addToQueue(r);
}
The first time I run it - no problem. If I try to "refresh" my data by calling the same method over again, the app will hang up with the InfiniteProgress dialog spinning forever. Its almost like the first network request is not ever really completing, and then the second one kind of conflicts. Any ideas what I'm doing wrong?
By default duplicate requests to the exact same URL are disabled, try invoking setDuplicatesSuppotred(true) on the connection request.
For future reference, what fixed this for me was to use
NetworkManager.getInstance().addToQueueAndWait(r);
instead. That cleared up most of my problems.
I stucked with the same problem and none of solutions worked. However, I did it this way:
final NetworkManager nm = NetworkManager.getInstance();
nm.setTimeout(3000);
then
protected void postResponse() {
...
nm.shutdown();
}
and call was made as
nm.addToQueueAndWait(request);
Maybe the fact that NetworkManager was made final did the job, but I put "shutdown" just for sure. It worked for me

GWT-Platform + SmartGWT: Chained pop-up windows

I got a login window which, depending on the RPC response, will show me another Window. What I'm wondering is how to initialize the second Window. I tried onReset and onReveal methods but is like they are never triggered, In addition, when resetting values, using destroy() will kill my Window permanently. I ended up with the following solution, but I feel is not too efficient, can someone recommend me a way to do it?
public void onSuccess(LoginResult result) {
if(result.getResponse().equalsIgnoreCase("OK")){
getView().getUsernameField().setValue("");
getView().getPasswordField().setValue("");
getView().getWindow().hide();
memberWindow.setUsername(username);
memberWindow.loadAppointments(new Date());
((Window) memberWindow.getWidget()).show();
}else{
SC.say("Error", "Login failed because: " + result);
}
Try out something like this
#Override
public void onSuccess(LoginResult result) {
CurrentUser currentUser = new CurrentUser(getView().getUserName());
LoginAuthenticatedEvent.fire(eventBus, currentUser);
// notice the place manager call. The transitions between the pages are
// done in GWTP through PlaceManager.revealPlace(PlaceRequest) call.
PlaceRequest placeRequest = new PlaceRequest(NameTokens.mainPage);
getPlaceManager().revealPlace(placeRequest);
}
Check out
src\au\com\uptick\serendipity\client\presenter\SignInPagePresenter.java
from the Serendipity application a sample of login done with SmartGWT + GWTP :
http://code.google.com/p/crmdipity/downloads/detail?name=Serendipity-0.6.0.zip&can=2&q=

J2ME/Java: Referencing StringBuffer through Threads

This question might be long, but I want to provide much information.
Overview: I'm creating a Stock Quotes Ticker app for Blackberry. But I'm having problems with my StringBuffer that contains an individual Stock information.
Process: My app connects to our server via SocketConnection. The server sends out a formatted set of strings that contains the latest Stock trade. So whenever a new trade happens, the server will send out an individual Stock Quote of that trade. Through an InputStream I am able to read that information and place each character in a StringBuffer that is referenced by Threads. By parsing based on char3 I am able to determine a set of stock quote/information.
char1 - to separate data
char3 - means end of a stock quote/information
sample stock quote format sent out by our server:
stock_quote_name(char 1)some_data(char1)some_data(char1)(char3)
My app then parses that stock quote to compare certain data and formats it how it will look like when displayed in the screen. When trades happen gradually(slow) the app works perfectly. However..
Problem: When trades happen too quickly and almost at the same time, My app is not able to handle the information sent efficiently. The StringBuffer has its contents combined with the next trade. Meaning Two stock information in one StringBuffer.
field should be: Stock_quote_name some_data some_data
sample of what's happening: Stock_quote_name some_data some_dataStock_quote_name some_data some_data
here's my code for this part:
while (-1 != (data = is.read()))
{
sb.append((char)data);
while(3 != (data = is.read()))
{
sb.append((char)data);
}
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
try
{
synchronized(UiApplication.getEventLock())
{
SetStringBuffer(sb);
DisplayStringBuffer();
RefreshStringBuffer();
}
} catch (Exception e)
{
System.out.println("Error in setting stringbuffer: " + e.toString());
}
}
});
}
public synchronized void DisplayStringBuffer()
{
try
{
//parse sb - string buffer
......
}
catch(Exception ex)
{
System.out.println("error in DisplayStringBuffer(): " + ex.toString());
}
}
public synchronized void SetStringBuffer(StringBuffer dataBuffer)
{
this.sb =dataBuffer;
System.out.println(sb);
}
public synchronized void RefreshStringBuffer()
{
this.sb.delete(0, this.sb.length());
}
From what I can see, when trades happen very fast, The StringBuffer is not refreshed immediately and still has the contents of the previous trade, when i try to put new data.
My Question is:
Do you guys have any suggestion on how i can put data into the StringBuffer, without the next information being appended to the first content
The part where you read data is synchronized, but the part where you append data to the buffer is not. If you are reusing the same StringBuffer each time, you will have a race condition.
Well, since you are using invokeLater to set / display / clear your StringBuffer, you're right, there's nothing blocking you from going back up to the next read() call and modifying that StringBuffer before you're able to dispaly it.
Obviously you're not reading from the input stream from your event thread, so in order to update the UI you need to either use invokeLater OR synchronize on the event lock, but you're doing both.
If you want to use invokeLater, then you'll need to make sure you don't append to your StringBuffer with your input stream reader thread while your event thread is attempting to display the data in the UI. Thus, you'll probably look into creating a new StringBuffer for each Runnable.
If you want to synchronize on the event lock, something like this could work (which might work better as it avoids unnecessary garbage creation with creating new Runnables / StringBuffers).
while (-1 != (data = is.read()))
{
sb.append((char)data);
while(3 != (data = is.read()))
{
sb.append((char)data);
}
synchronized(UiApplication.getEventLock())
{
SetStringBuffer(sb);
DisplayStringBuffer();
RefreshStringBuffer();
}
}
You can't reuse the same StringBuffer for each stock quote because you are reading the next stock quote before the UI thread has finished displaying the last one. Remember, you are writing to the StringBuffer on one thread, and reading the StringBuffer on another. There is no guarantee that the UI thread (reading thread) has called RefreshStringBuffer() before the loop iterates and starts appending the next stock quote to the StringBuffer.
Use a collection of Strings instead.
java.util.queue<String> q = new java.util.concurrent.ConcurrentLinkedQueue<String>();
then do q.add(sb.toString()); when your done putting the quote into sb.
to display the quotes
public void DisplayStockQuote() {
while(!q.isEmpty()) {
String s = q.poll();
// display s
try
{
//parse s - string containing stock quote
......
}
catch(Exception ex)
{
System.out.println("error in DisplayStringBuffer(): " + ex.toString());
}
}
}
Call that method from your UI thread instead of
SetStringBuffer(sb);
DisplayStringBuffer();
RefreshStringBuffer();
I suggest you try avoiding the extra thread and go with Bradley's solution. But if you really want to have two threads, something like this will work.

Resources