Gwt localStorageEvent - cross tab communication - java

I want to make cross tab communication. On that i would like to use localStorage / sessionStorage.
Class1 contains this part of code:
Storage stockStore = Storage.getLocalStorageIfSupported();
if (stockStore != null) {
stockStore.setItem("newLoad", "123");
}
Class2 contains this part of code:
Storage stockStore = Storage.getLocalStorageIfSupported();
if (stockStore != null) {
stockStore.addStorageEventHandler(new StorageEvent.Handler() {
public void onStorageChange(StorageEvent event) {
Log.println("Heureka!");
}
});
}
Code in class one is called when user push button.
So when user have open two tabs and he push button (inside tab 1) which will invoke code in class1. Then event is fired and "Herueka" is written but only in his actual tab (tab 1).
It should be written in both tabs (tab 1 and tab 2). So this is not working.
url of tab 1: http://127.0.0.1:8888/#loads
url of tab 2: http://127.0.0.1:8888/index.html#lights
Tabs have same session and i am using FF 47 for testing.
Everything is compiled trough eclipse local jetty server and super dev mode is used.

One workaround has been found since the previous answer: https://github.com/gwtproject/gwt/issues/9205 (I have not tried it)
That said, I have confirmed following to work:
private static native void addTrueStorageHandler(Handler h) /*-{
$wnd.addEventListener('storage', function(e) {
h.#com.google.gwt.storage.client.StorageEvent.Handler::onStorageChange(Lco m/google/gwt/storage/client/StorageEvent;)(e);
});
}-*/;
The above function should be called to register the Handler. This just creates a javascript anonymous function to call the Handler. It completely bypasses the entire Storage GWT mechanism.

I can confirm that and I had the same issues trying to utilize the events on the local storage.
Unfortunately the storage events do not seem to work across tabs.
At least I was unable to have them reliably fire for all tabs and I also needed to exclude the current tab from the event (this tab fired it and does not need to be notified).
What I did as a workaround is to create a class, which handles signaling using the local storage.
It is quite a lot of code, which is tied to an internal project, but I will try to outline how it is done to help you getting started.
I don't rely on events here, but use a Timer with scheduleRepeating to periodically check the local storage every 3 seconds.
Every tab needs a different ID, which you can generate e.g. using Random.
If I want to signal something I set an entry in the local storage.
The key of this entry is the signal name, the value is the id of the tab setting the signal.
So when the Timer is executed, I check for an entry with the signal name. If I find one, I check the value to be different than the id of the current tab as I don't want to notify the signaling tab for its own event.
You just need to keep in mind to remove the entry from the local storage on shutdown of your application or after a reasonable amount of time. Otherwise you might encounter the old entry on a fresh start of your application and execute the signal handler again.
After all it is not perfect as there is always the Timer running every 3 seconds and the signaled tab can also take up to 3 s to pick up the signal. However it was the only way for me to get a reliable signaling between different tabs.

Related

Java (Android) File object too slow for AsyncTask

In an Android application I'm developing, I need to create a backup of a file and check on start up if it is identical to a remote file, if they are have different bytes then overwrite the backup with the remote file, afterwards check if they are the same, if they are identical return true. To do this I have the following process:
if(!backupFileExists(){
backupFile.createNewFile();
}
if(!checkBackupAndRemoteFilesAreIdentical()){ <----First Time
if(overwriteBackupFileWithRemoteFile()){
if(checkBackupAndRemoteFilesAreIdentical()){ <---- Second Time
return true;
}
}
}
return false;
The problem is when i run the code with AsyncTask, the second time i run checkBackupAndRemoteFilesAreIdentical() the value of the backupFile.length() hasn't updated, so it returns 0 which then returns false.
However if I add Thread.sleep(5000) the value of the backupFile.length() has time to update, it is successful and returns true.
Is there anyway to have this work without the Thread.sleep(5000) ?
This is normal, a network operation will take longer than loading your first activity.
If your 1st activity requires this file (or a portion of it) in order to work, then, you need to get it from the server (yes! in an async task) and update an object that your application will rely on. Decouple your application logic/design from source of data.
This problem is a generic one, what we do in general, we get as little data from the server for the first activity to work, then, when the user navigate to other screens, the data will be available at this stage.
I would suggest to think about your application data design. Which Server Data you need in every step and how this will be updated. If you are the owner of the server, and you are able to create new endpoints, try to design fast endpoints, less data, expose 'http head' operations to get only the content-length, ...
If you have more details on what you're trying to build, I will be more than happy to go through specifics.

POS Customer Display (Web Based Application) [duplicate]

I was searching for a way how to communicate between multiple tabs or windows in a browser (on the same domain, not CORS) without leaving traces. There were several solutions:
using the window object
postMessage
cookies
localStorage
The first is probably the worst solution - you need to open a window from your current window and then you can communicate only as long as you keep the windows open. If you reload the page in any of the windows, you most likely lost the communication.
The second approach, using postMessage, probably enables cross-origin communication, but it suffers the same problem as the first approach. You need to maintain a window object.
The third way, using cookies, store some data in the browser, which can effectively look like sending a message to all windows on the same domain, but the problem is that you can never know if all tabs read the "message" already or not before cleaning up. You have to implement some sort of timeout to read the cookie periodically. Furthermore you are limited by maximum cookie length, which is 4 KB.
The fourth solution, using localStorage, seemed to overcome the limitations of cookies, and it can be even listen-to using events. How to use it is described in the accepted answer.
You may better use BroadcastChannel for this purpose. See other answers below. Yet if you still prefer to use localstorage for communication between tabs, do it this way:
In order to get notified when a tab sends a message to other tabs, you simply need to bind on 'storage' event. In all tabs, do this:
$(window).on('storage', message_receive);
The function message_receive will be called every time you set any value of localStorage in any other tab. The event listener contains also the data newly set to localStorage, so you don't even need to parse localStorage object itself. This is very handy because you can reset the value just right after it was set, to effectively clean up any traces. Here are functions for messaging:
// use local storage for messaging. Set message in local storage and clear it right away
// This is a safe way how to communicate with other tabs while not leaving any traces
//
function message_broadcast(message)
{
localStorage.setItem('message',JSON.stringify(message));
localStorage.removeItem('message');
}
// receive message
//
function message_receive(ev)
{
if (ev.originalEvent.key!='message') return; // ignore other keys
var message=JSON.parse(ev.originalEvent.newValue);
if (!message) return; // ignore empty msg or msg reset
// here you act on messages.
// you can send objects like { 'command': 'doit', 'data': 'abcd' }
if (message.command == 'doit') alert(message.data);
// etc.
}
So now once your tabs bind on the onstorage event, and you have these two functions implemented, you can simply broadcast a message to other tabs calling, for example:
message_broadcast({'command':'reset'})
Remember that sending the exact same message twice will be propagated only once, so if you need to repeat messages, add some unique identifier to them, like
message_broadcast({'command':'reset', 'uid': (new Date).getTime()+Math.random()})
Also remember that the current tab which broadcasts the message doesn't actually receive it, only other tabs or windows on the same domain.
You may ask what happens if the user loads a different webpage or closes his tab just after the setItem() call before the removeItem(). Well, from my own testing the browser puts unloading on hold until the entire function message_broadcast() is finished. I tested to put some very long for() cycle in there and it still waited for the cycle to finish before closing. If the user kills the tab just in-between, then the browser won't have enough time to save the message to disk, thus this approach seems to me like safe way how to send messages without any traces.
There is a modern API dedicated for this purpose - Broadcast Channel
It is as easy as:
var bc = new BroadcastChannel('test_channel');
bc.postMessage('This is a test message.'); /* send */
bc.onmessage = function (ev) { console.log(ev); } /* receive */
There is no need for the message to be just a DOMString. Any kind of object can be sent.
Probably, apart from API cleanness, it is the main benefit of this API - no object stringification.
It is currently supported only in Chrome and Firefox, but you can find a polyfill that uses localStorage.
For those searching for a solution not based on jQuery, this is a plain JavaScript version of the solution provided by Thomas M:
window.addEventListener("storage", message_receive);
function message_broadcast(message) {
localStorage.setItem('message',JSON.stringify(message));
}
function message_receive(ev) {
if (ev.key == 'message') {
var message=JSON.parse(ev.newValue);
}
}
Checkout AcrossTabs - Easy communication between cross-origin browser tabs. It uses a combination of the postMessage and sessionStorage APIs to make communication much easier and reliable.
There are different approaches and each one has its own advantages and disadvantages. Let’s discuss each:
LocalStorage
Pros:
Web storage can be viewed simplistically as an improvement on cookies, providing much greater storage capacity. If you look at the Mozilla source code we can see that 5120 KB (5 MB which equals 2.5 million characters on Chrome) is the default storage size for an entire domain. This gives you considerably more space to work with than a typical 4 KB cookie.
The data is not sent back to the server for every HTTP request (HTML, images, JavaScript, CSS, etc.) - reducing the amount of traffic between client and server.
The data stored in localStorage persists until explicitly deleted. Changes made are saved and available for all current and future visits to the site.
Cons:
It works on same-origin policy. So, data stored will only be able available on the same origin.
Cookies
Pros:
Compared to others, there's nothing AFAIK.
Cons:
The 4 KB limit is for the entire cookie, including name, value, expiry date, etc. To support most browsers, keep the name under 4000 bytes, and the overall cookie size under 4093 bytes.
The data is sent back to the server for every HTTP request (HTML, images, JavaScript, CSS, etc.) - increasing the amount of traffic between client and server.
Typically, the following are allowed:
300 cookies in total
4096 bytes per cookie
20 cookies per domain
81920 bytes per domain (given 20 cookies of the maximum size 4096 = 81920 bytes.)
sessionStorage
Pros:
It is similar to localStorage.
Changes are only available per window (or tab in browsers like Chrome and Firefox). Changes made are saved and available for the current page, as well as future visits to the site on the same window. Once the window is closed, the storage is deleted
Cons:
The data is available only inside the window/tab in which it was set.
The data is not persistent, i.e., it will be lost once the window/tab is closed.
Like localStorage, tt works on same-origin policy. So, data stored will only be able available on the same origin.
PostMessage
Pros:
Safely enables cross-origin communication.
As a data point, the WebKit implementation (used by Safari and Chrome) doesn't currently enforce any limits (other than those imposed by running out of memory).
Cons:
Need to open a window from the current window and then can communicate only as long as you keep the windows open.
Security concerns - Sending strings via postMessage is that you will pick up other postMessage events published by other JavaScript plugins, so be sure to implement a targetOrigin and a sanity check for the data being passed on to the messages listener.
A combination of PostMessage + SessionStorage
Using postMessage to communicate between multiple tabs and at the same time using sessionStorage in all the newly opened tabs/windows to persist data being passed. Data will be persisted as long as the tabs/windows remain opened. So, even if the opener tab/window gets closed, the opened tabs/windows will have the entire data even after getting refreshed.
I have written a JavaScript library for this, named AcrossTabs which uses postMessage API to communicate between cross-origin tabs/windows and sessionStorage to persist the opened tabs/windows identity as long as they live.
I've created a library sysend.js for sending messages between browser tabs and windows. The library doesn't have any external dependencies.
You can use it for communication between tabs/windows in the same browser and domain. The library uses BroadcastChannel, if supported, or storage event from localStorage.
The API is very simple:
sysend.on('foo', function(data) {
console.log(data);
});
sysend.broadcast('foo', {message: 'Hello'});
sysend.broadcast('foo', "hello");
sysend.broadcast('foo', ["hello", "world"]);
sysend.broadcast('foo'); // empty notification
When your browser supports BroadcastChannel it sends a literal object (but it's in fact auto-serialized by the browser) and if not, it's serialized to JSON first and deserialized on another end.
The recent version also has a helper API to create a proxy for cross-domain communication (it requires a single HTML file on the target domain).
Here is a demo.
The new version also supports cross-domain communication, if you include a special proxy.html file on the target domain and call proxy function from the source domain:
sysend.proxy('https://target.com');
(proxy.html is a very simple HTML file, that only have one script tag with the library).
If you want two-way communication you need to do the same on other domains.
NOTE: If you will implement the same functionality using localStorage, there is an issue in Internet Explorer. The storage event is sent to the same window, which triggers the event and for other browsers, it's only invoked for other tabs/windows.
Another method that people should consider using is shared workers. I know it's a cutting-edge concept, but you can create a relay on a shared worker that is much faster than localstorage, and doesn't require a relationship between the parent/child window, as long as you're on the same origin.
See my answer here for some discussion I made about this.
There's a tiny open-source component to synchronise and communicate between tabs/windows of the same origin (disclaimer - I'm one of the contributors!) based around localStorage.
TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);
TabUtils.OnBroadcastMessage("eventName", function (eventDataString) {
DoSomething();
});
TabUtils.CallOnce("lockname", function () {
alert("I run only once across multiple tabs");
});
P.S.: I took the liberty to recommend it here since most of the "lock/mutex/sync" components fail on websocket connections when events happen almost simultaneously.
I wrote an article on this on my blog: Sharing sessionStorage data across browser tabs.
Using a library, I created storageManager. You can achieve this as follows:
storageManager.savePermanentData('data', 'key'): //saves permanent data
storageManager.saveSyncedSessionData('data', 'key'); //saves session data to all opened tabs
storageManager.saveSessionData('data', 'key'); //saves session data to current tab only
storageManager.getData('key'); //retrieves data
There are other convenient methods as well to handle other scenarios as well.
This is a development storage part of Tomas M's answer for Chrome. We must add a listener:
window.addEventListener("storage", (e)=> { console.log(e) } );
Load/save the item in storage will not fire this event - we must trigger it manually by
window.dispatchEvent( new Event('storage') ); // THIS IS IMPORTANT ON CHROME
And now, all open tabs will receive the event.

Accessing an Array/Collection of Custom Objects from All Activities, Android

I'm a just diving head first into Android Studio and am working on my first app which is a sort of surfing log application. Basically it is to keep track of various weather conditions each time the user goes out surfing.
I have created a Java class "Session" with the following fields:
Date (Date)
Location (String)
Tide height (float)
Surf size (float)
more to come, keeping it simple for now.
My application will flow as follows:
There will be a Main home screen activity, with buttons, and also a small tableview that displays your 3 most recent sessions.
Buttons including:
New Session: this takes you to a new activity with various text inputs for each of the above fields, a date selector... and a save button.
My Sessions: this will take you to a tableview where you can view all of your past sessions. You can organize them by location, date, surf size...
Thus my question:
What is the best practice to pass all this data between the various activities?
To me the most obvious way to go about this is to have a central ArrayList that gets loaded in the main activity and then this gets somehow passed around to all of the subsequent activities. It contains all the sessions that you have ever created.
So lets say I tap the "new session" button on the main screen. This takes me to the new Session activity. I enter all the fields and hit save. I would like to take this data, create a new session object and then add this to the array back in Main Activity. So far my research tells me to pass all this data back using a bundle and the intent.putextra() technique. However it seems cumbersome. Is there not a better approach where I could just create a new object of my Session class and then append it to the central array?
Again let's say I tap the 'my sessions' button from the main activity. I would like this to load up a new activity which is a tableview that allows the user to scroll through all of their previously created sessions and tap on one to view the details.
I've done a bit of research and it seems there are various ways of going about this, I've read up a bit on Singletons, I've looked into creating some sort of static class that I could then reference from multiple activities, I've read about parcelable and serializable...
Perhaps though someone with some android experience could shed some light on the most efficient and standard way of accomplishing what I would like to do.
Again I want to reiterate that this Array or collection of "Sessions" is going to be the center of the app. Pretty much every activity I implement down the road is going to be using this data in someway or another. Whether it's displaying it a tableview which can be sorted in different ways, to running statistical analysis on it, to displaying pins on a map of each location...
I think you want to keep your data in a database and use a pub/sub workflow to keep track of it.
For example, you can use Firebase. Everytime a part of your application does an update (even a different user on a different device) any other part of your code can listen to that change and capture it.
Firebase is just an example, RethinkDB, MongoDB all have this option.
It is not a good option to use a data structure that is on your app: it will become really messy if it needs to be shared between different parts of your app, and impossible if you need other users to be aware of the change.
I think you should go for an event-based library like RxJava2. you can easily subscribe to a bus (subject) which emits all data created up till now.to keep other app's components up-to-date.
for you specefic use case. there is sth great in RXjava2 is called Replay Subject
Replay Subject:
It emits all the items of the source Observable, regardless of when the subscriber subscribes
a simple implementation would look like
public class RxReplayBus {
private static ReplaySubject<Object> subject = ReplaySubject.create();
private RxReplayBus() {
// hidden constructor
}
public static Disposable subscribe(#NonNull Consumer<Object> action) {
return subject.subscribe(action);
}
public static void publish(#NonNull Object message) {
subject.onNext(message);
}
}
from your component subscribe :
disposable = RxReplayBus.subscribe(new Consumer<Object>() {
#Override
public void accept(Object o) throws Exception {
}
});
and unsubscribe using :
disposable.dispose();

Displaying a message one time only

I want to find a way to display a notification (like a JOptionpane, a JLabel or any other type) only one time after a user launches my application that is formed in a.jar file.
By only one time, I mean that the user gets a one-time notification after the first use, then for every following times my application runs, this notification should not appear.
My application uses Java Swing. Is there a hint how to make a message pops up from the main JFrame for example?
You simply need to know whether this application has already been running in that environment before or not. A simple way to do that is to:
Check whether some file with a particular name exists in the working directory
if it doesn't: show your notification, then create the file
if it does: don't show your notification
Sample Java code:
private static void notify() {
final File file = new File(".launched");
if(!file.exists()) {
// show your notification HERE
file.createNewFile();
}
}
Check for stored value on disk indicating the message has been shown
If not, show the message and store the value on disk.
You can do that by setting up one preference. I think is the most straight forward way to do it. Use the preferences class.
The preferences are loaded at starting, then you ask if your "boolean_first_use" is false or true. After that you set it to false, as you know that the user is having that first time message. So next time, it will not fire the notification.
http://www.vogella.com/tutorials/JavaPreferences/article.html
http://www.javaranch.com/journal/2002/10/preferences.html

How can I force Vaadin v8 to update the screen?

I have a small Vaadin v8 application that has several input fields (comboboxes, selectgroups, etc...). The content of most of these is determined by the chosen content of the first ComboBox. However, when I select something in it, all the others stay blank until I click one, at which point they all update. This is not desired behaviour, but I assume it's being caused by the server-side being up to date but not updating the client side view. (Even when adding requestRepaint() in my first Combobox's ValueChangeListener)
There must be some method to force Vaadin to get the data I want it to display even if no other components are clicked?
EDIT
I'm not allowed to post answers to my own question so soon, so I'm putting it here temporarily:
I found that there's a javascript method that synchs client and server.
myComponent.getApplication().getMainWindow().executeJavaScript("javascript:vaadin.forceSync();");
The only problem I have now is that the ValueChangeListener on one of my comboboxes still only fires when I click another combobox (or the same one twice). It's the weirdest thing because the second combobox, when loaded, fires it's event perfectly.
Is the first ComboBox in "immediate" mode?
If not, it probably should be : component.setImmediate(true).
See https://vaadin.com/book/-/page/components.selection.html
I had the same problem, see below how it could be done in version 8.0.5 (from 2017):
#Push
public class WebUi extends UI {
public void fireComponentUpdated() {
getUI().push();
}
}
There is a hack you can use if you have set a datasource for your componets that forces vaadin to re-render them. I use this for updating tables that have dynamic data
yourcomponent.setContainerDataSource(yourcomponent.getContainerDataSource());
Did you requestRepaint on the correct components?
Keep in mind that requestRepaint marks the component as dirty but doesn't mean it will be repainted - a client can only be refreshed when it makes a request to the server.
See this thread https://vaadin.com/forum/-/message_boards/view_message/231271 for more information about your options (it deals with UI refreshes due to background thread processing).
In Vaadin 7 it is enough to put this line in main UI.init(VaadinRequest) method:
UI.getCurrent().setPollInterval( 1000 );
if you want to refresh your UI (in this case) every second. This way you instruct UI to poll server for changes in defined interval.
Beware, excessive server traffic might be a problem if you have lot of users that use your application at the same time.
In Vaadin 6 you will have to play with ProgressIndicator (which could be invisible if you want) and try to do the similar what UI.getCurrent().setPollInterval(int) in Vaadin 7 does.

Categories

Resources