So I have implemented Observer Pattern. In the update function of my Observer class, I want to update the progressbar. The problem is that it instantly updates to the maximum value. I don't get an loading animation. The application calculates some edges. After the calculation of the edges, they will be drawn on the screen.
My observers are the following :
GenerateEdgeBottom
GenerateEdgeLeft
GenerateEdgeRight
I also have a KochManager and an javaFX class.
I make my progressbars in the javaFX class. In the javaFX class is a function that binds all the progressbars.
Part of my javaFX class
public void binding(GenerateEdgeLeft leftEdges, GenerateEdgeRight rightEdges, GenerateEdgeBottom bottomEdges) {
progressBarLeft.progressProperty().unbind();
progressBarLeft.setProgress(0);
progressBarRight.progressProperty().unbind();
progressBarRight.setProgress(0);
progressBarBottom.progressProperty().unbind();
progressBarBottom.setProgress(0);
progressBarLeft.progressProperty().bind(leftEdges.progressProperty());
progressBarRight.progressProperty().bind(rightEdges.progressProperty());
progressBarBottom.progressProperty().bind(bottomEdges.progressProperty());
lblLeft.textProperty().bind(leftEdges.messageProperty());
lblRight.textProperty().bind(rightEdges.messageProperty());
lblBottom.textProperty().bind(bottomEdges.messageProperty());
}
My kochmanager
public void changeLevel(int nxt) {
edges.clear();
koch.setLevel(nxt);
this.leftEdgeTask = new GenerateEdgeLeft(koch);
this.bottomEdgeTask = new GenerateEdgeBottom(koch);
this.rightEdgeTask = new GenerateEdgeRight(koch);
tsCalc.init();
tsCalc.setBegin("Begin calculating");
//System.out.println("test 3");
Platform.runLater(() -> {
//System.out.println("test 4");
application.binding(leftEdgeTask,rightEdgeTask,bottomEdgeTask);
futures.clear();
Future<?> f = pool2.submit(bottomEdgeTask);
Future<?> f2 =pool2.submit(leftEdgeTask);
Future<?> f3 = pool2.submit(rightEdgeTask);
futures.add(f);
futures.add(f2);
futures.add(f3);
try {
edges.addAll(leftEdgeTask.get());
edges.addAll(rightEdgeTask.get());
edges.addAll(bottomEdgeTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//System.out.println("test 5");
koch.getEdges().clear();
tsCalc.setEnd("End calculating");
application.setTextNrEdges("" + koch.getNrOfEdges());
application.setTextCalc(tsCalc.toString());
application.requestDrawEdges();
drawEdges();
});
}
My GenerateEdgeLeft
public class GenerateEdgeLeft extends Task<ArrayList<Edge>> implements Observer {
private KochFractal koch;
//private KochManager manager;
private int numberOfEdges;
private int counter;
private ArrayList<Edge> edges = new ArrayList<>();
public GenerateEdgeLeft(KochFractal koch){
this.koch = koch;
numberOfEdges = koch.getNrOfEdges() / 3;
this.koch.addObserver(this);
}
public GenerateEdgeLeft() {
//edges.size
}
public ArrayList<Edge> getResult() {
return edges;
}
#Override
public ArrayList<Edge> call() throws Exception {
counter = 0;
System.out.println("Thread Started Left");
koch.generateLeftEdge();
edges = koch.getEdges();
return edges;
}
#Override
public void update(Observable o, Object arg) {
edges.add((Edge) arg);
counter++;
updateProgress(counter, numberOfEdges);
updateMessage( "Left : " + counter);
}
}
Related
I'm calculating three different values at the same time which is depending on the level state of my application, so I'm using Tasks for a faster calculation. However if I want to change the level during calculation it's not possible, because all the buttons are disabled during the calculation.
I've found out that I cannot cancel the tasks because my GUI freezes until the tasks are finished. Therefore I cannot cancel tasks, because they are already finished
My GUI has this buttonclick event
buttonIncreaseLevel.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.runLater(new Runnable() {
#Override
public void run() {
increaseLevelButtonActionPerformed(event);
}
});
}
});
It increases a level and starts this method
createTask(taskLeftEdge,EdgeType.LEFT);
createTask(taskBottomEdge,EdgeType.BOTTOM);
createTask(taskRightEdge,EdgeType.RIGHT);
pool.submit(taskRightEdge);
pool.submit(taskBottomEdge);
pool.submit(taskLeftEdge);
new Thread(new Runnable() {
#Override
public void run() {
try {
System.out.println("[Future] Started to wait for task (future)");
// A task is also its own future
ArrayList taskR = (ArrayList) taskRightEdge.get();
ArrayList taskB = (ArrayList) taskBottomEdge.get();
ArrayList taskL = (ArrayList) taskLeftEdge.get();
System.out.println("[Future] " + taskR);
System.out.println("[Future] " + taskB);
System.out.println("[Future] " + taskL);
} catch (InterruptedException | ExecutionException ex) {
Logger.getLogger(FUN3KochFractalFX.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
).start();
The method for creating a task
private void createTask(Task task, EdgeType type){
if (task != null) {
application.cancelProgress(type);
task.cancel();
}
// There's a new task that performs some work
switch(type){
case LEFT:
taskLeftEdge = new EdgeGeneratorTask("Task: " + taskNumber, type, this, application);
task = taskLeftEdge;
edges.addAll(koch.generateLeftEdge());
break;
case BOTTOM:
taskBottomEdge = new EdgeGeneratorTask("Task: " + taskNumber, type, this, application);
task = taskBottomEdge;
edges.addAll(koch.generateBottomEdge());
break;
case RIGHT:
taskRightEdge = new EdgeGeneratorTask("Task: " + taskNumber, type, this, application);
task = taskRightEdge;
edges.addAll(koch.generateRightEdge());
break;
}
taskNumber++;
application.setProgress( task, type);
}
And the Task itself. The class Kochfractal is where the calculations are made
private static final Logger LOG = Logger.getLogger(EdgeGeneratorTask.class.getName());
private final String id;
private EdgeType type;
private KochFractal calc;
private FUN3KochFractalFX application;
public EdgeGeneratorTask(String id, EdgeType type, KochManager manager, FUN3KochFractalFX main) {
this.id = id;
this.type = type;
calc = new KochFractal(manager);
application = main;
}
#Override
protected ArrayList<Edge> call() throws Exception {
ArrayList<Edge> edges = new ArrayList<>();
switch(type){
case LEFT:
edges = calc.generateLeftEdge();
break;
case BOTTOM:
edges = calc.generateBottomEdge();
break;
case RIGHT:
edges = calc.generateRightEdge();
break;
}
int MAX = edges.size();
for (int i = 1; i <= MAX; i++) {
if (isCancelled()) {
break;
}
updateProgress(i, MAX);
updateMessage(id + " " + i);
}
return edges;
}
I expect these threads to be cancelled, with an action on my JavaFX GUI.
The threads should run on a background Thread instead of the GUI thread itself
My JFrame containing an embedded single graph (Graphstream) freezes when I try to update it in a loop that calls Thread,sleep(). I have tried using the same update on a standalone-graph (displayed on it's own) and it works as expected.
I have a single graph embedded in JFrame as follows (AppGraph.java):
public static ViewPanel init(){
graph.addAttribute("ui.stylesheet", styleSheet);
graph.setAutoCreate(true);
graph.setStrict(false);
graph.addAttribute("ui.quality");
graph.addAttribute("ui.antialias");
initGraph();
initNodes(graph);
return attachViewPanel();
}
private static ViewPanel attachViewPanel() {
Viewer viewer = new Viewer(graph, Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD);
viewer.enableAutoLayout();
return viewer.addDefaultView(false);
}
private static void initGraph(){
FileSource fs = new FileSourceDOT();
String graph_filename = "graph.gv";
String absolute_path = System.getProperty("user.home") + File.separator + graph_filename;
fs.addSink(graph);
try {
fs.readAll(absolute_path);
} catch (IOException | NullPointerException e) {
e.printStackTrace();
} finally {
fs.removeSink(graph);
}
}
Then this is called in the JFrame class as below:
/*AppWindow.java
* Set up graph
*/
GridBagConstraints graphConstraints = new GridBagConstraints();
graphConstraints.fill = GridBagConstraints.BOTH;
graphConstraints.gridx = 0;
graphConstraints.gridy = 1;
graphConstraints.weightx = 0.5;
graphConstraints.weighty = 0.5;
graphConstraints.gridwidth = 4;
graphConstraints.gridheight = GridBagConstraints.RELATIVE;
add(AppGraph.init(), graphConstraints);`
On the JFrame are buttons for different search algorithms like BFS. During the execution of these algorithms, edges traversed are colored at fixed time intervals to create a sort of animation effect as shown below:
//BFSAlgorithm.java
private void callBFS(Node startNode, Node goalNode) {
startNode.setAttribute("parent", "null");
startNode.setAttribute("level", 0);
startNode.setAttribute("visited?");
LinkedList<Node> queueFrontier = new LinkedList<>();
int level = 1;
queueFrontier.addLast(startNode);
while (!queueFrontier.isEmpty()) {
System.out.println("Level: " + (level - 1));
LinkedList<Node> next = new LinkedList<>();
for (Node node : queueFrontier) {
if (node == goalNode) {
System.out.println(node.getId() + ": Found Found Found!!!");
if (node != startNode) {
colorEdge(node);
}
return;
}
System.out.print(node.getId() + " visited \t");
if (node != startNode) {
colorEdge(node);
}
for (Edge edge : node.getEdgeSet()) {
Node opposite = edge.getOpposite(node);
if (!opposite.hasAttribute("visited?")) {
System.out.print(opposite.getId() + " enqueued \t");
opposite.setAttribute("level", level);
opposite.setAttribute("parent", node);
opposite.setAttribute("visited?");
next.addLast(opposite);
}
}
System.out.print("\n");
}
level++;
queueFrontier = next;
sleep();
}
}
private void colorEdge(Node node) {
Edge visitedEdge = node.getEdgeBetween(node.getAttribute("parent", Node.class));
visitedEdge.setAttribute("ui.color", 0.5);
sleep();
}
private void sleep() {
try {
Thread.sleep(AppWindow.speed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
This BFSAlgorithm implements DynamicAlgorithm and extends SinkAdapter. I have extended the SinkAdapter to enable it to interact with the View as the algorithm runs. When I call the BFSAlgorithm, while the algorithm runs and the various println statements are delayed by sleep(), the GUI freezes and is unresponsive until after execution before all the visited edges are then colored. I tried implementing ViewerListener in my AppGraph.java as is documented on the graphstream documentation but it only resulted in an infinite loop that crashed the application:
/*...init() method from AppGraph.java*/
ProxyPipe fromViewer = viewer.newThreadProxyOnGraphicGraph();
fromViewer.addSink(graph);
fromViewer.pump();
while(loop) {
fromViewer.pump(); //
}
Like #Frakool and #MadProgrammer suggested in the comments, if anyone is having similar issues, using SwingWorker and Swing Timer will provide the desired results. According to the documentation:
In general, we recommend using Swing timers rather than general-purpose timers for GUI-related tasks because Swing timers all share the same, pre-existing timer thread and the GUI-related task automatically executes on the event-dispatch thread. However, you might use a general-purpose timer if you don't plan on touching the GUI from the timer, or need to perform lengthy processing.
Here's how I used it to stop the gui freezing. I created a private inner SwingWorker class that uses a Swing Timer as below:
private class BFSTask extends SwingWorker<LinkedList<Node>, Node>{
private ArrayList<Node> visitedList;
private int visitedIndex = 0;
private boolean traversalDone = false;
private Timer traversal = new Timer(AppWindow.speed, new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
Node lastVisited = visitedList.get(visitedIndex);
Edge visitedEdge = lastVisited.getEdgeBetween(lastVisited.getAttribute("parent", Node.class));
visitedEdge.setAttribute("ui.color", 0.5);
visitedIndex++;
if(visitedIndex >= visitedList.size()){
traversal.stop();
traversalDone = true;
if(BFSAlgorithm.this.getPathToGoal() != null){
startTimer();
}
}
}
});
#Override
protected LinkedList<Node> doInBackground() throws Exception {
Node found = publishNodeBreadthFirst(getStartNode(), getGoalNode());
if (found != null) {
return getPathToGoal(found);
} else{
return null;
}
}
#Override
protected void process(List<Node> list) {
visitedList = (ArrayList<Node>) list;
traversal.start();
}
#Override
protected void done() {
try {
BFSAlgorithm.this.pathToGoal = get();
if(traversalDone && BFSAlgorithm.this.getPathToGoal() != null){
startTimer();
}
if(BFSAlgorithm.this.getPathToGoal() == null){
throw new NullPointerException("Goal Not Found.");
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (NullPointerException e){
JOptionPane.showMessageDialog(getAppWindow(), "Goal Node Not Found!", "Error", JOptionPane.ERROR_MESSAGE);
getAppWindow().disableExceptClear();
getAppWindow().changeStatus("Goal node not found");
}
}
private LinkedList<Node> getPathToGoal(Node found) {
LinkedList<Node> path = new LinkedList<>();
Node parent = found.getAttribute("parent");
path.addLast(found);
while (parent != getStartNode()){
path.addLast(parent);
parent = parent.getAttribute("parent");
}
return path;
}
}
This question already has answers here:
Swing Progress Bar updates via Worker to EventDispatch thread
(2 answers)
Closed 7 years ago.
All the coding runs, my only problem is that the progress bar doesn't display the value changes. I set the values, but if I run it the value set, doesn't display on the progress bar. (The bar doesn't fill up)
public void logIn() {
try {
while (true) {
calculate();
System.out.println(loadingProgress);
if (loadingProgress < 100) {
mGUI.pbLoading.setValue(loadingProgress);
Thread.sleep(random.nextInt(5000) + 100);
} else {
mGUI.pbLoading.setValue(100);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void calculate() {
random = new Random();
loadingProgress = loadingProgress + random.nextInt(9) + 1;
}
The calculation has to be done in an extra thread. This is because the currently running thread is blocking the repaint of the UI until the calculation is done.
The problem is outside your posted code. I used your code to create a working example:
package snippet;
import java.util.Random;
public class Snippet {
public class ValueAccepter {
public void setValue(int loadingProgress) {
System.out.println(loadingProgress);
}
}
public class MGUI {
public final ValueAccepter pbLoading = new ValueAccepter();
}
private int loadingProgress;
private Random random;
private MGUI mGUI = new MGUI();
public static void main(String[] args) {
new Snippet().logIn();
}
public void logIn() {
try {
while (true) {
calculate();
System.out.println(loadingProgress);
if (loadingProgress < 100) {
mGUI.pbLoading.setValue(loadingProgress);
Thread.sleep(random.nextInt(5000) + 100);
} else {
mGUI.pbLoading.setValue(100);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void calculate() {
random = new Random();
loadingProgress = loadingProgress + random.nextInt(9) + 1;
}
}
I think you have a problem with your swing-threads. Make sure, that mGUI.pbLoading.setValue is returning as expected.
You have to use SwingWorker to execute graphical user interface operations and calculations otherwise the event dispatch thread won't be able to repaint. Here is a progress bar demo code by Oracle.
So my code works just the way I want it the only issue I'm having is this.. Basically I am having a main class which controls gates on a railroad track, when a train is approaching or crossing the track from either 1 of two tracks the gates should close. The only issue I'm having is the statements for when a gate opens or closes spam like 3-5 times everytime it does something so if the gate is closing it will go..
GATE: Closing
GATE: Closing
GATE: Closing
GATE: Closing
GATE: Closing
GATE: Closed
I'm wondering why this is occuring, here is my code for the Gate class and Main class
public class Gate {
private boolean isClosed = false;
private boolean closing = false;
private boolean opening = false;
public Gate(){
}
public void close(){
if(!(isClosing() == true)){
Runnable task = new Runnable() {
public void run() {
try {
setClosing(true);
setOpening(false);
System.out.println("GATE: Closing");
Thread.sleep(400);
System.out.println("GATE: Closed");
setClosed(true);
setClosing(false);
}catch(Exception ex){
}
}
};
new Thread(task, "closeThread").start();
}
}
public void open(){
if(!(isOpening() == true)){
Runnable task = new Runnable() {
public void run() {
try {
setOpening(true);
System.out.println("GATE: Opening");
Thread.sleep(400);
setOpening(false);
if(closing == false){
setClosed(false);
System.out.println("GATE: Opened");
}
}catch(Exception ex){
}
}
};
new Thread(task, "openThread").start();
}
}
public boolean isClosed(){
return isClosed;
}
public boolean isClosing(){
return closing;
}
public boolean isOpening(){
return opening;
}
public synchronized void setClosing(boolean t){
closing = t;
}
public synchronized void setOpening(boolean t){
opening = t;
}
public synchronized void setClosed(boolean t){
isClosed = t;
}
}
public class Controller {
public static void main(String[] args){
Track t1 = new Track("Track 1");
Track t2 = new Track("Track 2");
Gate g = new Gate();
t1.simulateTrack();
t2.simulateTrack();
do{
System.out.print("");
if((t1.isApproaching() || t1.isCrossing()) || (t2.isApproaching() || t2.isCrossing())){
if(!g.isClosed() && !g.isClosing()){
g.close();
}
}else if(g.isClosed() && !g.isOpening()){
g.open();
}
}while((t1.isSimulating() || t2.isSimulating()));
}
}
Also the code for Track
import java.security.SecureRandom;
public class Track {
private static final SecureRandom gen = new SecureRandom() ;
private boolean approaching = false;
private boolean atCrossing = false;
private boolean simulating = false;
private String trackName = "";
public Track(String n){
trackName = n;
}
public void simulateTrack(){
Runnable task = new Runnable() {
public void run() {
try {
setSimulating(true);
for(int i = 0; i < 10; i++){
Thread.sleep((gen.nextInt(5000) + 2500));
setApproaching(true);
System.out.println(trackName + ": Train is now approaching.");
Thread.sleep((gen.nextInt(5000) + 3500));
setCrossing(true);
setApproaching(false);
System.out.println(trackName + ": Train is now crossing.");
Thread.sleep((gen.nextInt(1000) + 1000));
setCrossing(false);
System.out.println(trackName + ": Train has left.");
}
setSimulating(false);
} catch (Exception ex) {
}
}
};
new Thread(task, "simulationThread").start();
}
public boolean isApproaching(){
return approaching;
}
public boolean isCrossing(){
return atCrossing;
}
public boolean isSimulating(){
return simulating;
}
public synchronized void setSimulating(boolean t){
simulating = t;
}
public synchronized void setApproaching(boolean t){
approaching = t;
}
public synchronized void setCrossing(boolean t){
atCrossing = t;
}
}
This is just an idea:
By shooting the close() logic on a background thread you lose the atomicity. The main's do loop can go around 5 times before it gives up the control of the main thread and one of the "closeThread"s start executing. Don't you see multiple "GATE: Closed"s as well?
Try this (not tested, sorry):
public synchronized void close() { // added synchornized
if (!isClosing()) { // read: "if not closing"
setClosing(true); // set closing so next time close() is called it is a no op
setOpening(false); // close other loopholes so the state is correct
System.out.println("GATE: Closing");
// we're in closing state now, because the close method is almost finished
// start the actual closing sequence
Runnable task = new Runnable() {
public void run() {
try {
Thread.sleep(400);
System.out.println("GATE: Closed");
setClosed(true);
setClosing(false);
}catch(Exception ex){
}
}
};
new Thread(task, "closeThread").start();
}
}
You'll need to modify open() the same way, so that the invariants are always kept. Checking and setting the closing and opening flags are mutually exclusive, that's what you get by placing synchronized on both of them.
I have an ExectorService and the following code, everything is working fine with the futures and the concurrency. However, when I try to update my progress bar from SomeClass(), it seems to only update the UI after the invokeAll() is complete...basically the progress bar only updates once everything is complete which deems it useless.
How can I resolve this? I've looked at CompletionServices as well as SwingWorkers but I don't know how to apply them to my code. Any assistance will be appreciated.
class SomeClass() {
private static class Result {
private final String someVar;
public Result(String code) {
this.someVar = code;
}
}
public static Result compute(Object obj) {
// ... compute stuff
someVar = "computedResult";
return Result(someVar);
}
public someFunction() {
List<Callable<Result>> tasks = new ArrayList<Callable<Result>>();
for (Object f : listOfObjects) {
Callable<Result> c = new Callable<Result>() {
#Override
public Result call() throws Exception {
someClassUI.jProgressBar.setValue(50);
return compute(file);
}
};
tasks.add(c);
}
List<Callable<Result>> tasks = new ArrayList<Callable<Result>>();
List<Future<Result>> results = executorService.invokeAll(tasks);
for (Future<Result> fr : results) {
String value = fr.get().resultValue;
}
}
}
class SomeClassUI {
public static jProgressBar;
public someClassUI() {
jProgressBar = new JProgressBar(0,100);
}
private void button1ActionPerformed(ActionEvent e) {
SomeClass theClass = new SomeClass();
theClass.someFunction();
}
}
edit: edited to add some extra code to help understanding
You're accessing a Swing component from a thread other than the event dispatch thread. That is forbidden by the Swing threading policy.
Use this code to update the progress bar from the background thread:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
someClassUI.jProgressBar.setValue(50);
}
});
Read more about concurrency in swing in the official swing tutorial.
I've been testing with code similar to yours, until I realized the following:
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException
Executes the given tasks, returning a list of Futures holding their status and results when all complete.
That "when all complete" is what is causing the behaviour of the progress bar. In other words, if you get the list of Future only when all tasks complete, then, obviously, iterating over them and updating the bar would be so fast that you see only the last update, when the bar is full.
What you could do, like I did, is calling submit for each of your tasks, and add the Futures individually to a list.
The example code below has been tested, and works here. You should be able to adapt it to your own purposes.
Listener interface:
public interface UpdateListener {
void update(double percent);
}
Task Executor:
public class SomeClass {
// instance variables
private UpdateListener listener;
private ExecutorService executor;
/** Parameter constructor of objects of class SomeClass. */
public SomeClass(UpdateListener l) {
listener = l;
executor = Executors.newFixedThreadPool(4);
}
/** */
public void doIt() throws InterruptedException, ExecutionException {
int numOfTasks = 5, completedTasks = 0;
List<Future<Integer>> results = new ArrayList<>();
// Submit each of your tasks. Here I create them manually.
for (int i = 0; i < numOfTasks; ++i) {
final int I = i;
Callable<Integer> c = new Callable<Integer>() {
#Override
public Integer call() throws Exception {
try {
Thread.sleep((long) I * 1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return new Integer(I);
}
};
results.add(executor.submit(c));
}
// Retrieve individual results and update progress bar.
for (Future<Integer> fr : results) {
Integer i = fr.get();
++completedTasks;
listener.update((double) completedTasks / numOfTasks);
}
}
}
UI class:
public class SomeClassUI implements Runnable, UpdateListener {
// instance variables
private JProgressBar bar;
private JFrame frame;
private SomeClass t;
/** Empty constructor of objects of class SomeClassUI. */
public SomeClassUI() {
t = new SomeClass(this);
}
/** Builds the interface. */
public void run() {
bar = new JProgressBar(0, 100);
bar.setStringPainted(true);
JPanel panel = new JPanel(new FlowLayout());
panel.setPreferredSize(new Dimension(200, 100));
panel.add(bar);
frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
/** Method from the interface. Updates the progress bar. */
#Overrides
public void update(double percent) {
final double PERCENT = percent;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int v = (int) (100 * PERCENT);
bar.setValue(v);
}
});
}
/** Tests the program. */
public void go() {
SwingUtilities.invokeLater(this);
try {
t.doIt();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main() {
new SomeClassUI().go();
}
}