Is there a way of opening a full web page using Java, I have to check the time taken in opening full web page in a Java user end application, I have tried this code:
URL ur = new URL("http://www.google.com/");
HttpURLConnection yc =(HttpURLConnection) ur.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(yc.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
but this gives me the source code of the url... that's useless for me!
as I need to record the time taken by a webpage to load on my desktop by making a Java application.
Everything is on client site!
Here is a JavaFX based sample:
import java.util.Date;
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.concurrent.Worker.State;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.layout.*;
import javafx.scene.web.WebEngine;
import javafx.stage.Stage;
/** times the loading of a page in a WebEngine */
public class PageLoadTiming extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
final Label instructions = new Label(
"Loads a web page into a JavaFX WebEngine. " +
"The first page loaded will take a bit longer than subsequent page loads due to WebEngine intialization.\n" +
"Once a given url has been loaded, subsequent loads of the same url will be quick as url resources have been cached on the client."
);
instructions.setWrapText(true);
instructions.setStyle("-fx-font-size: 14px");
// configure some controls.
final WebEngine engine = new WebEngine();
final TextField location = new TextField("http://docs.oracle.com/javafx/2/get_started/jfxpub-get_started.htm");
final Button go = new Button("Go");
final Date start = new Date();
go.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent arg0) {
engine.load(location.getText());
start.setTime(new Date().getTime());
}
});
go.setDefaultButton(true);
final Label timeTaken = new Label();
// configure help tooltips.
go.setTooltip(new Tooltip("Start timing the load of a page at the entered location."));
location.setTooltip(new Tooltip("Enter the location whose page loading is to be timed."));
timeTaken.setTooltip(new Tooltip("Current loading state and time taken to load the last page."));
// monitor the page load status and update the time taken appropriately.
engine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
#Override public void changed(ObservableValue<? extends State> state, State oldState, State newState) {
switch (newState) {
case SUCCEEDED: timeTaken.setText(((new Date().getTime()) - start.getTime()) + "ms"); break;
default: timeTaken.setText(newState.toString());
}
}
});
// layout the controls.
HBox controls = HBoxBuilder.create().spacing(10).children(new Label("Location"), location, go, timeTaken).build();
HBox.setHgrow(location, Priority.ALWAYS);
timeTaken.setMinWidth(120);
// layout the scene.
VBox layout = new VBox(10);
layout.setStyle("-fx-padding: 10; -fx-background-color: cornsilk; -fx-font-size: 20px;");
layout.getChildren().addAll(controls, instructions);
stage.setTitle("Page load timing");
stage.getIcons().add(new Image("http://icons.iconarchive.com/icons/aha-soft/perfect-time/48/Hourglass-icon.png"));
stage.setScene(new Scene(layout, 1000, 110));
stage.show();
// trigger loading the default page.
go.fire();
}
}
Yeah home is right that should do the trick. But this way is much more simpler... Although a bit erroneous it should serve fine for benchmark purposes.
JEditorPane editorPane = new JEditorPane();
editorPane.setPage(new URL("http://www.google.com"));
The scripts are still rendered on the screen if you just use this snippet, although it can be worked around I don't think you will need to bother with that since you need to just benchmark loading time... However the results may be varying very slightly since it doesn't take time to run the JavaScript.
You can use Cobra
You can use plain Swing also
You can also use JavaFX to do it like this:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.web.WebView;
public class MyWebView extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) {
final WebView wv = new WebView();
wv.getEngine().load("http://www.google.com");
primaryStage.setScene(new Scene(wv));
primaryStage.show();
}
}
To open a web page using java , try the following code :
import java.io.*;
public class b {
public static void main(String args[])throws IOException
{
String downloadURL="<url>";
java.awt.Desktop myNewBrowserDesktop = java.awt.Desktop.getDesktop();
try
{
java.net.URI myNewLocation = new java.net.URI(downloadURL);
myNewBrowserDesktop.browse(myNewLocation);
}
catch(Exception e)
{
}
}
}
This will open the web page in your default browser . As for the time taken to load part , have a look here . There are a lot of suggestions there .
Related
I am able to get snapshots of video when played using the following;
mediaView.snapshot(null, null);
Now I want to take snapshots with certain duration gaps like the following:
public class Main extends Application {
Media media = null;
MediaPlayer mediaPlayer = null;
MediaView mediaView = null;
public static void main(String[] args) {
launch(args);
}
public void start(final Stage primaryStage) {
primaryStage.setTitle("Video Snapshot");
media = new Media(new File("filePathTo.mp4").toURI().toString());
mediaPlayer = new MediaPlayer(media);
mediaView = new MediaView(mediaPlayer);
Pane pane = new Pane();
mediaPlayer.setOnReady(new Runnable() {
public void run() {
writeToFile(0.0);
writeToFile(1000.0);
writeToFile(10000.0);;
writeToFile(50000.0);
}
});
Scene scene = new Scene(pane, 640, 480, Color.BLACK);
primaryStage.setScene(scene);
mediaPlayer.play();
primaryStage.show();
}
private void writeToFile(Double double1) {
mediaView.getMediaPlayer().seek( Duration.millis(double1) );
WritableImage writableImage = mediaView.snapshot(null, null);
ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
try {
ImageIO.write( SwingFXUtils.fromFXImage( writableImage, new BufferedImage(200,200,BufferedImage.TYPE_INT_RGB) ), "png", byteOutput );
byteOutput.writeTo(new FileOutputStream( Paths.get(double1.toString()).toFile() + ".png" ));
} catch (IOException e) {
e.printStackTrace();
}
}
}
But now I get the same image and not different images, although the writableImage objects returned are different. Check the files 0.0.png, 1000.0.png, 10000.0.png, 50000.0.png files. All appears same. How could I get different snapshots?
Most likely, the problem is that the new pixels aren't rendered by the time you take the snapshot. You need to give JavaFX time to render the video. The following example works for me:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;
import javafx.util.Duration;
import javax.imageio.ImageIO;
public class Main extends Application {
public void start(Stage primaryStage) {
var player = new MediaPlayer(new Media(Path.of("video.mp4").toUri().toString()));
player.setAutoPlay(true);
var view = new MediaView(player);
player.setOnPlaying(() -> {
snapshotVideo(view, 0)
.thenCompose(unused -> snapshotVideo(view, 1000))
.thenCompose(unused -> snapshotVideo(view, 2000))
.thenCompose(unused -> snapshotVideo(view, 3000));
});
primaryStage.setScene(new Scene(new Pane(view), 1000, 650));
primaryStage.setTitle("Video Snapshot");
primaryStage.show();
}
private CompletableFuture<Void> snapshotVideo(MediaView view, double seekMillis) {
var future = new CompletableFuture<Void>();
view.getMediaPlayer().seek(Duration.millis(seekMillis));
Platform.runLater(() -> {
var fxImage = view.snapshot(null, null);
var bufImage = SwingFXUtils.fromFXImage(fxImage, null);
var filename = String.format("snapshot-%.0f.png", seekMillis);
try (var out = Files.newOutputStream(Path.of(filename))) {
ImageIO.write(bufImage, "png", out);
future.complete(null);
} catch (IOException ex) {
ex.printStackTrace();
future.completeExceptionally(ex);
}
});
return future;
}
}
It uses a combination of Platform#runLater(Runnable) and CompletableFuture.
It seeks to the desired time and then runs the snapshot+save code in a runLater call. This is an attempt to give JavaFX time to render the new seeked-to frame. I don't know if this is guaranteed to work. If I'm not mistaken, the JavaFX media implementation has one or more background threads for processing the audio/video. But I don't know if those threads can "fall behind". Also, it's possible that seeking far enough ahead could lead to buffering. So, you might have to handle that by listening to the status property of the MediaPlayer.
It uses CompletableFuture to make sure the next snapshot is not taken until the previous one has been taken and saved. It would probably be a good idea to move the image-saving code to a background thread. The important part, I think, is that the snapshot has been fully taken before you try to take the next one.
I also start taking these snapshots when the player is playing, not just ready. Not sure if that matters. I'm not even sure that the player needs to be playing for this to work.
And finally, the JavaFX Media API is probably not the best tool to use for this job. If I wanted frames from an MP4 video at different times, I would try to find a library that can just grab those frames straight from the file.
Thank you #Slaw for your answer. When I tried your code, it gave different results every time I ran it. The problem is definitely that the new pixels aren't rendered by the time the snapshots are taken. runLater and CompletableFuture are both providing some time for the process to render which sometimes are sufficient as maybe what happened in your system. But they are no way aware of if the process is really ready for snapshot-ing. What worked for me is that I replaced the player.setOnPlaying with the following:
player.setOnMarker(event -> {
snapshotVideo(view, event.getMarker().getValue().toMillis());
});
And also modified the snapshotVideo method in the following way:
private void snapshotVideo(MediaView view, double duration) {
var fxImage = view.snapshot(null, null);
var bufImage = SwingFXUtils.fromFXImage(fxImage, null);
var filename = String.format("snapshot-%.0f.png", duration);
try (var out = Files.newOutputStream(Path.of(filename))) {
ImageIO.write(bufImage, "png", out);
} catch (IOException ex) {
ex.printStackTrace();
}
}
What I did was removed both runLater and CompletableFuture. I am depending on Player.setOnMarker() method for assurance of the process to be ready for snapshot-ing.
Using new AudioClip(s) with s an external UI String will download the clip from a remote server. This can take several seconds.
If this is done on the JavaFX Application thread, the application will likely become unresponsive.
What is the preferred solution to prevent this long download from being on the JavaFX App thread?
Example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.media.AudioClip;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
long time = System.currentTimeMillis();
AudioClip sound = new AudioClip("https://dl.dropboxusercontent.com/s/qq8vvfqx2l8sljb/Dice%201.wav?dl=0");
System.out.println("Time taken (ms): " + (System.currentTimeMillis() - time));
primaryStage.setScene(new Scene(new StackPane(), 300, 250));
primaryStage.show();
}
}
Creating the AudioClip this way freezes the JavaFX thread for around 2-3 seconds on my computer.
any operation that is expected to take more than some milliseconds needs to be moved out of the JavaFX Application Thread, because otherwise, it will freeze your UI until it's done, if I were to load an audio file from a remote URL I would do it in a new Thread as follows :
import java.util.concurrent.ExecutionException;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.scene.media.AudioClip;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane(new ProgressIndicator());
Button play = new Button("play");
Task<AudioClip> loadAudio = new Task<AudioClip>() {
#Override
protected AudioClip call() throws Exception {
return new AudioClip("https://dl.dropboxusercontent.com/s/qq8vvfqx2l8sljb/Dice%201.wav?dl=0");
}
};
loadAudio.setOnSucceeded(successEvent -> {
play.setOnAction(actionEvent -> {
try {
loadAudio.get().play();
} catch (InterruptedException | ExecutionException x) {
//Handle Exceptions
x.printStackTrace();
}
});
root.getChildren().setAll(play);
});
new Thread(loadAudio).start();
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}
Note that in some cases, your app will need to indicate to the users that it is doing something in the background and they will need to wait until the operation is done, in this example I used a simple progress indicator while the audio is being downloaded from the remote URL.
note: I edited the solution using Task (the previous version works fine as well as this version as I have tested)
my apologies if this is an easy thing for you, but my mind boggles. After several years of not programming at all, I am working on a pet project (2d tile based game engine) where I would like to use Java FX headless in order to make use of the graphics capabilities.
I have understood from here and here
that you need to a Java FX Application in order to have the graphics system initialized.
So I basically took the ImageViewer example and implemented Runnable:
package net.ck.game.test;
import java.io.BufferedReader;
import java.util.ArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Logger;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ImageTest extends Application implements Runnable {
protected static final Logger logger = (Logger) LogManager.getLogger(ImageTest.class);
BufferedReader x;
#Override public void start(#SuppressWarnings("exports") Stage stage) {
logger.error(Thread.currentThread().getName() + ", executing run() method!");
Image standardImage = new Image("file:graphics/image1.png");
logger.error("image height image1: "+ standardImage.getHeight());
logger.error("image width image1:" + standardImage.getWidth());
Image movingImage = new Image("file:graphics/image2.png");
ArrayList<Image> images = new ArrayList<Image>();
images.add(movingImage);
images.add(standardImage);
ImageView iv1 = new ImageView();
iv1.setImage(standardImage);
ImageView iv2 = new ImageView();
iv2.setImage(movingImage);
Group root = new Group();
Scene scene = new Scene(root);
scene.setFill(Color.BLACK);
HBox box = new HBox();
box.getChildren().add(iv1);
box.getChildren().add(iv2);
root.getChildren().add(box);
stage.setTitle("ImageView");
stage.setWidth(415);
stage.setHeight(200);
stage.setScene(scene);
stage.sizeToScene();
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void run()
{
Application.launch(ImageTest.class);
}
}
When I run this as its own application, this works fine and displays the two images I want it to display.
When I run it like this in the "game" constructor:
public class Game {
private boolean animated;
public boolean isAnimated() {
return animated;
}
public void setAnimated(boolean animated) {
this.animated = animated;
}
public Game() {
setAnimated(true);
if (isAnimated() == true)
{
ImageTest imageTest = new ImageTest();
new Thread(imageTest).start();
}
}
There are no errors, ImageTest runs in its own thread, the application window opens, but it is empty.
I do not understand this at all, why is that?
Can someone pleaese shed some light on this?
UPDATE:
I had different working contexts by accident. Fixing this fixed the problem.
UPDATE: I had different working contexts by accident. Fixing this fixed the problem.
I am developing an application with several TextField objects that need to be updated to reflect changes in associated back-end properties. The TextFields are not editable, only the back-end may change their content.
As I understand, the correct way about this is to run the heavy computation on a separate thread so as not to block the UI. I did this using javafx.concurrent.Task and communicated a single value back to the JavaFX thread using updateMessage(), which worked well. However, I need more than one value to be updated as the back-end does its crunching.
Since the back-end values are stored as JavaFX properties, I tried simply binding them to the textProperty of each GUI element and let the bindings do the work. This doesn't work, however; after running for a few moments, the TextFields stop updating even though the back-end task is still running. No exceptions are raised.
I also tried using Platform.runLater() to actively update the TextFields rather than binding. The issue here is that the runLater() tasks are scheduled faster than the platform can run them, and so the GUI becomes sluggish and needs to time to "catch up" even after the back-end task is finished.
I found a few questions on here:
Logger entries translated to the UI stops being updated with time
Multithreading in JavaFX hangs the UI
but my issue persists.
In summary: I have a back-end making changes to properties, and I want those changes to appear on the GUI. The back-end is a genetic algorithm, so its operation is broken down into discrete generations. What I would like is for the TextFields to refresh at least once in between generations, even if this delays the next generation. It is more important that the GUI responds well than that the GA runs fast.
I can post a few code examples if I haven't made the issue clear.
UPDATE
I managed to do it following James_D's suggestion. To solve the issue of the back-end having to wait for the console to print, I implemented a buffered console of sorts. It stores the strings to print in a StringBuffer and actually appends them to the TextArea when a flush() method is called. I used an AtomicBoolean to prevent the next generation from happening until the flush is complete, as it is done by a Platform.runLater() runnable. Also note that this solution is incredibly slow.
Not sure if I completely understand, but I think this may help.
Using Platform.runLater(...) is an appropriate approach for this.
The trick to avoiding flooding the FX Application Thread is to use an Atomic variable to store the value you're interested in. In the Platform.runLater method, retrieve it and set it to a sentinel value. From your background thread, update the Atomic variable, but only issue a new Platform.runLater if it's been set back to its sentinel value.
I figured this out by looking at the source code for Task. Have a look at how the updateMessage method (line 1131 at the time of writing) is implemented.
Here's an example which uses the same technique. This just has a (busy) background thread which counts as fast as it can, updating an IntegerProperty. An observer watches that property and updates an AtomicInteger with the new value. If the current value of the AtomicInteger is -1, it schedules a Platform.runLater.
In the Platform.runLater, I retrieve the value of the AtomicInteger and use it to update a Label, setting the value back to -1 in the process. This signals that I am ready for another UI update.
import java.text.NumberFormat;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class ConcurrentModel extends Application {
#Override
public void start(Stage primaryStage) {
final AtomicInteger count = new AtomicInteger(-1);
final AnchorPane root = new AnchorPane();
final Label label = new Label();
final Model model = new Model();
final NumberFormat formatter = NumberFormat.getIntegerInstance();
formatter.setGroupingUsed(true);
model.intProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(final ObservableValue<? extends Number> observable,
final Number oldValue, final Number newValue) {
if (count.getAndSet(newValue.intValue()) == -1) {
Platform.runLater(new Runnable() {
#Override
public void run() {
long value = count.getAndSet(-1);
label.setText(formatter.format(value));
}
});
}
}
});
final Button startButton = new Button("Start");
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
model.start();
}
});
AnchorPane.setTopAnchor(label, 10.0);
AnchorPane.setLeftAnchor(label, 10.0);
AnchorPane.setBottomAnchor(startButton, 10.0);
AnchorPane.setLeftAnchor(startButton, 10.0);
root.getChildren().addAll(label, startButton);
Scene scene = new Scene(root, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public class Model extends Thread {
private IntegerProperty intProperty;
public Model() {
intProperty = new SimpleIntegerProperty(this, "int", 0);
setDaemon(true);
}
public int getInt() {
return intProperty.get();
}
public IntegerProperty intProperty() {
return intProperty;
}
#Override
public void run() {
while (true) {
intProperty.set(intProperty.get() + 1);
}
}
}
}
If you really want to "drive" the back end from the UI: that is throttle the speed of the backend implementation so you see all updates, consider using an AnimationTimer. An AnimationTimer has a handle(...) which is called once per frame render. So you could block the back-end implementation (for example by using a blocking queue) and release it once per invocation of the handle method. The handle(...) method is invoked on the FX Application Thread.
The handle(...) method takes a parameter which is a timestamp (in nanoseconds), so you can use that to slow the updates further, if once per frame is too fast.
For example:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
final BlockingQueue<String> messageQueue = new ArrayBlockingQueue<>(1);
TextArea console = new TextArea();
Button startButton = new Button("Start");
startButton.setOnAction(event -> {
MessageProducer producer = new MessageProducer(messageQueue);
Thread t = new Thread(producer);
t.setDaemon(true);
t.start();
});
final LongProperty lastUpdate = new SimpleLongProperty();
final long minUpdateInterval = 0 ; // nanoseconds. Set to higher number to slow output.
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
if (now - lastUpdate.get() > minUpdateInterval) {
final String message = messageQueue.poll();
if (message != null) {
console.appendText("\n" + message);
}
lastUpdate.set(now);
}
}
};
timer.start();
HBox controls = new HBox(5, startButton);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
BorderPane root = new BorderPane(console, null, null, controls, null);
Scene scene = new Scene(root,600,400);
primaryStage.setScene(scene);
primaryStage.show();
}
private static class MessageProducer implements Runnable {
private final BlockingQueue<String> messageQueue ;
public MessageProducer(BlockingQueue<String> messageQueue) {
this.messageQueue = messageQueue ;
}
#Override
public void run() {
long messageCount = 0 ;
try {
while (true) {
final String message = "Message " + (++messageCount);
messageQueue.put(message);
}
} catch (InterruptedException exc) {
System.out.println("Message producer interrupted: exiting.");
}
}
}
public static void main(String[] args) {
launch(args);
}
}
The best way to performing this is by usage of Task in JavaFx. This is be by far the best technique I've come across to update UI Controls in JavaFx.
Task task = new Task<Void>() {
#Override public Void call() {
static final int max = 1000000;
for (int i=1; i<=max; i++) {
updateProgress(i, max);
}
return null;
}
};
ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(task.progressProperty());
new Thread(task).start();
I have implemented a java program which executes a http request. Now if this request were to give an alert box, how can I capture it?
The code is as below
String url= "http://www.quettamotors.pk/car_details.php?id=(script)alert(1)(/script)"; //added the round brackets as it is not allowing angular brackets.
URL myurl= new URL(url);
Runtime.getRuntime().exec("rundll32 url.dll , FileProtocolHandler " + myurl);
Help please!! Thanks
Use
String query = myurl.getQuery();
It will return you id=(script)alert(1)(/script) as String. Now you can split it by =;
Provide an alert handler to a WebEngine.
The following is Java 8 code which will log JavaScript alert messages to the console:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class AlertReporter extends Application {
#Override
public void start(final Stage stage) {
final WebView webView = new WebView();
webView.getEngine().load(
"http://www.w3schools.com/js/tryit.asp?filename=tryjs_alert"
);
webView.getEngine().setOnAlert(
event -> System.out.println(event.getData())
);
stage.setScene(new Scene(webView));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}