Server Sent Events Not Working Using JavaFX WebView - reactjs

I have developed a single page application with React and MobX. To get some progress information from backend code such as copying a file progress, I use Server Sent Events and EventSource at javascript code. In any browser, I can get the event messages and show progresses successfully. React gets changes from the EventSource onmessage event and renders the changes on the screen. Here is how I add EventSource to my js code. (For the ones who ask if the message format of eventsource is correct? Yes, it is. It is working on browsers.)
Code:
fetchEvents(url) {
let evtSource = new EventSource(url);
evtSource.onmessage = (message) => {
const data = JSON.parse(message.data);
//assign data to an observable variable
}
}
However, I need my single page application to be embeded in a Java application. For this, I use JavaFX WebEngine and WebView to load my React application. Every functionalities work well except Server Sent Events and EventSource messages. onmessage(), onopen(), onerror() methods are not called. Thus, I can't get the changes of data to be shown on the screen. I can't get any information via EventSource at my javascript code. Here is the JavaFX code;
Code:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("localhost");
WebView myBrowser = new WebView();
WebEngine myWebEngine = myBrowser.getEngine();
myWebEngine.setJavaScriptEnabled(true);
myWebEngine.load("http://localhost:8080");
StackPane root = new StackPane();
root.getChildren().addAll(myBrowser, reloadButton);
primaryStage.setScene(new Scene(root, 800, 640));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Does JavaFX WebView not support EventSource and Server Sent Events? I am curious about is there any way to make SSE work in JavaFX WebView? Or are there any other possible solutions to embed my single page application in Java code with EventSource and Server Sent Events working well.
Thank you.

Related

WebBrowser issue (add infinite progress in web browser)

It takes a couple of seconds to load an url in webbrowser. Sometimes it takes more time to load. How to add infinite progress like that of connectionRequest in webbrowser?
#Override
protected void postWebView(Form f) {
WebBrowser wb = new WebBrowser();
if (businessWebsiteUrl != null && !businessWebsiteUrl.equals("")) {
wb.setURL("http://" + businessWebsiteUrl);
f.add(BorderLayout.CENTER, wb);
} else {
}
f.revalidate();
}
What I did but doesnot work
protected void beforeWebView(Form f) {
ip = new InfiniteProgress();
f.add(BorderLayout.CENTER, FlowLayout.encloseCenterMiddle(ip));
}
That's inherently problematic since a browser is effectively a peer component and while we can now paint on top of peers in Android this is still not portable.
Even if it would I'm not sure if it would be a good idea.
Overall you have the following options:
Place a progress indicator above/below the browser component
Fetch the data separately as HTML with a progress and set the HTML which should be instant
Use JavaScript and potentially an iframe to indicate progress from within the browser component itself

Why Doesn't My Component Appear On Top of The HTML Page Or Media Player In Codename One

I've used a media player and I'm trying to do rendering on top of it with progress indication or buttons but the code isn't working.
E.g.:
findInfiniteProgress().setVisible(true);
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
if(findMediaPlayer().getMedia() != null && findMediaPlayer().getMedia().isPlaying()){
findInfiniteProgress(f).setVisible(false);
}else{
findInfiniteProgress(f).setVisible(true);
}
}
});
}
};
Codename One uses lightweight rendering which means all components are drawn in sequence on a single thread. Native widgets (peers) need to be drawn on the native thread and are always drawn on top, that is the secret to Codname Ones portability explained here.
Common peers in Codename One include: Web browser, media playback & native maps.
The workaround is to use Dialog which is effectively a separate Form so the current peer doesn't really render underneath.

Same functionality as HTTPClient in Codename one

I was wondering how I can achieve something like an HTTPClient.
I tried WebBrowser class but it seems that the execution continues even though the URL specified has not yet loaded.
public void testWebBrowser(){
final WebBrowser b = new WebBrowser(){
#Override
public void onLoad(String url) {
BrowserComponent c = (BrowserComponent)this.getInternal();
JavascriptContext ctx = new JavascriptContext(c);
// I want this Javascript context here
}
};
// just a test URL
b.setURL("http://youtube.com");
// Suppose to get the Javascript context here though it executes without waiting for the whole page to load
}
How can I get the JS Context from within a WebBrowser context? Like a synchronous execution
WebBrowser browser = new WebBrowser();
browser.setURL("someURL");
// wait execution till the whole page in "someURL" loads till it executes the next line
BrowserComponent c = (BrowserComponent)browser.getInternal();
JavascriptContext ctx = new JavascriptContext(c);
If I understand correctly you are trying to create a scraping solution?
That's probably not the ideal approach since this will actually create a web browser which you then need to automate with JavaScript. I would suggest you create a webservice that encapsulates the HttpClient functionality and drive it with ConnectionRequest. This way when the web site changes you can just fix your server in a way seamless to your installed base.

Wicket - Inter-session communication or Create new Request(Cycle) manually

I have some wicket panel store in a static Hashmap from different sessions, i want to do some like if some panel notifies the map, then the map notifies all other panel.
for example:
public class PanelMap{
private static Map<Long, List<MyPanel>> map = new HashMap<Long, List<MyPanel>>();
public static void subscribe(Long id, MyPanel panel){
if (!map.containsKey(id)){
map.put(id, new ArrayList<MyPanel>());
}
map.get(id).add(panel);
}
}
public static void notify(Long id, String notification){
if (map.containsKey(id)){
List<MyPanel> panels = map.get(id);
for(MyPanel panel : panels){
panel.newNotification(notification);
}
}
}
}
In Panel, newNotification(String notification) i want to send request to server and refresh my panel in browser.
public void String newNotification(String notification){
// do some business logic depends on notification
onMyRequest();
}
i've made some search among wicket behavior source files and about i found in AbstractDefaultAjaxBehavior i tried to make my own onRequest method inside my wicket panel as follows
private void onMyRequest(){
AjaxRequestTarget target = ((WebApplication)getApplication()).newAjaxRequestTarget(getPage());
target.add( _some_wicket_components_ );
RequestCycle.get().scheduleRequestHandlerAfterCurrent(target);
}
but all i did is some Ajax error in Wicket Ajax Debug about
Wicket.Ajax.Call.processComponent: Component with id _containerdiv_ was not found while trying to perform markup update.
ERROR: Cannot find element with id: _someComponentIdOnPanel_
(those components are exist)
How could i send my own request to server (or how can i get valid AjaxRequestTarget to update my components? )
Update: I need inter-session communication.
To update panels on different user's sessions, you obviously can't use the current AjaxRequestTarget as this in a way represents a single communication between the server and the requesting user of which another user's Browser has no way of knowing. (Very very basically spoken)
You could either use an AjaxSelfUpdatingTimerBehavior to poll for updates. This would generate new AjaxRequestTarget for every user at regular intervals that you can use to attach changed panels to. It's a very basic and simple implementation that will most likely impact your systems performance and generate quite some traffic.
The other way would be to use something like Atmosphere, which is supported by Wicket-Atmosphere (quickstart can be found here) and has some examples over at the wicket-library.com, but that's all I know about this.
Use Wicket event bus system. Have a look to the "Wicket events infrastructure" chapter of the free Wicket guide.
First you need to create one class to encapsulate the notification and the AjaxRequestTarget and pass them using the events infrastructure.
private class Notification {
private String message;
private AjaxRequestTarget target;
... constructor, getters, setters...
}
Then the panels that want to recive the event have to override onEvent method, something like this:
public void onEvent(IEvent<?> event) {
if (event.getPayload() instanceof Notification) {
Notification notification = (Notification) event.getPayload();
... do whatever you want before updating the panel ...
// Update the panel
notification.getTarget().add(this);
}
}
All the components will recive all the events that are send using Wicket events infrastructure. So you can send the event from any other panel using one method like this
protected void sendMessage(String message, AjaxRequestTarget target) {
send(getSession(), Broadcast.BREADTH, new Notification(message, target));
}
Remember that if you want to update the components using AJAX, you need to set setOutputMarkupId(true). And if it's a component that can be hidden and you want to make it visible using AJAX, then you need to set setOutputMarkupPlaceholderTag(true).

GWT form upload using BlobstoreService App Engine

I am using GWT and Google App Engine Java for my application. I have a profile screen where
user enters profile information like name, age and address, saves it and gets success or failure message. I developed this initial application using GWT-RPC and it worked fine. I had a new requirement where I have to store image of the user. I am using BlobstoreService to store images. This has created complications in the flow. I had to use FormPanel as it is the only way to do a FileUpload in GWT. The BlobStore service servlet expects a redirect on completion. As a result it cannot now return any status back to my GWT application once the profile is saved. Is there easy to store images using GWT along with other form fields and show a status message back to user once the profile is saved.
i struggled a lot with this problem until yesterday I figured out the solution with much help from Ikai Lan's blog. Basicaly what I did is follow his steps but with a few modifications because doing it exactly how he did it did'nt work for me:
Create a form panel : set encoding multipart, method post.
Make a GWT Remote Service that just has one method:public String getUploadURL() or something like that and in the IMPL write this:
BlobstoreService service = BlobstoreServiceFactory.getBlobstoreService();
return service.createUploadUrl("/XXX/YYY");
In XXX you must put your project path, for example mine is com.fer.pyn.PictureYourNews
In YYY you must put the servlet mapping name for a new servlet that we will have to create: I put XXX = BlobUploader, I created a BlobUploader extends HttpServlet and you have to update the web.xml.
Okey, so this is the weird part that I could'nt figure out, thing is that when we make a RPC call to getUploadURL() in the remote ervice from step 2 that returns a weird addres, like: '/_ah/img/eq871HJL_bYxhWQbTeYYoA' and that is the .fromAction you have to put in your form from step one. You need to update the form's action every time so i suggest the following:
public void initBlobStoreSession()
{
imageService.getBlobStoreUploadURL(new AsyncCallback()
{
#Override
public void onSuccess(String result) {
uploadFormPanel.setAction(result);
System.out.println("Upload Form Panel Action set");
}
#Override
public void onFailure(Throwable caught) {
//oops
}
});
}
So when you submit your fromPanel, IT WILL UPLOAD THE BLOB and you dont have to do anything, the tricky part is how to get the blob:
What you need to do now is create the YYY servlet we where talking about in step 4.
In the post method, this is important:
private BlobstoreService blobService = BlobstoreServiceFactory.getBlobstoreService();
Map<String, BlobKey> blobMap = blobService.getUploadedBlobs(request);
BlobKey blobKey = blobMap.get(UPLOAD_WIDJET_NAME);
UPLOAD_WIDJET_NAME is the .setName for the FileUpload widjet.
What you are doing there is getting a key for yout BLob so you can reference it later.
Our next step is showing the uploaded image back to the GWT layer:
//In the same post method from step 7
ImagesService imagesService = ImagesServiceFactory.getImagesService();
String imageURL = imagesService.getServingUrl(blobKey);
response.sendRedirect("/XXX/YYY?imgURL="+imageURL);
Now in the get method:
String imageUrl = request.getParameter("imgURL");
response.setHeader("Content-Type", "text/html");
response.getWriter().println(imageUrl);
We are done, now you just have to
uploadFormPanel.addSubmitCompleteHandler(new SubmitCompleteHandler() {
#Override
public void onSubmitComplete(SubmitCompleteEvent event) {
uploadFormPanel.reset();
initBlobStoreSession();
String imageUrl = event.getResults();
Image image = new Image();
image.setUrl(imageUrl);
//if you are using jetty, leave this on
//or else it wont work
//Don't use GWT.getModuleBaseURL(), it doesnt
//work well in development mode
imageUrl.replace("http://0.0.0.0:8888/", "");
System.out.println(imageUrl);
final PopupPanel imagePopup = new PopupPanel(true);
imagePopup.setWidget(image);
// Add some effects
imagePopup.setAnimationEnabled(true); // animate opening the image
imagePopup.setGlassEnabled(true); // darken everything under the image
imagePopup.setAutoHideEnabled(true); // close image when the user clicks
imagePopup.center(); // center the image
}
});
check out upload4gwt which address uploading in GWT on AppEngine.
(disclosure: I created upload4gwt; it's not mature yet, however may be useful)
I had the same problem. As a workaround I'm using a redirection to a servlet that print a status message for the client to parse.
I'm passing the websafe string representation of the key to that result servlet.
That's a bit hackey, I'd like someone to come with a better answer, or explain why the blobstore servlet have to redirect.
Yeah, things get more complicated with uploads in GWT.
You can save the form data and image in separate RPCs, and either include a status message in the response to the image upload, or fire off a 3rd RPC when the form returns to get any status or metadata you need.

Resources