issue in sending the socket output to GUI - java

I wrote this simple multi threaded chatroom, and I am trying to send the client/server output to GUI to display it in chatroom text area but I get null pointer exception at the following line:
output.write(line + "\n");
here is the full code for GUI:
import java.awt.*;
import javax.swing.*;
import javax.swing.JTextField;
import javax.swing.JFrame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import javax.swing.JButton;
import java.io.Writer;
public class GUI {
private JFrame frame;
private JButton btnSend, btnConnect;
private JTextArea txtChat;
private JTextField fldText, fldName;
private JList clientList;
private DefaultListModel listModel;
private JScrollPane sc, scClients;
private JPanel jpS2All, jpS2Client, jpS2Text;
private String Name;
private JLabel lblErr;
private Writer output;
public GUI(String Name, Writer output) {
this.Name = Name;
this.output = output;
}
public GUI() {
}
class handler implements ActionListener, MouseListener {
handler(String Name) {
}
handler() {
}
#Override
public void actionPerformed(ActionEvent e) {
clients(); //it seems this line made the error because it creates the
listModel.addElement(Name);//gui and sends the output to textSend actionlistener
//to display it in gui
//while the output is empty, right?
//is there any way to handle this?
}
#Override
public void mouseClicked(MouseEvent e) {
fldName.setText("");
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
} //end of handler
class textSend implements ActionListener {
textSend(String Name, Writer output) {
}
#Override
public void actionPerformed(ActionEvent e) {
String line = fldText.getText();
try {
output.write(line + "\n"); // the null exception error shows the
output.flush(); // error source at this line!
} catch (IOException ioe) {
txtChat.append("Other party hung up!");
}
String contenet = Name + ":" + output;
txtChat.append(contenet);
fldText.setText("");
}
}//end of textSend
public void creatServer() {
frame = new JFrame("enter");
frame.setBounds(50, 50, 300, 200);
btnConnect = new JButton("connect");
lblErr = new JLabel();
lblErr.setText("");
frame.add(btnConnect, BorderLayout.EAST);
fldName = new JTextField();
fldName.setText("enter your name");
fldName.addMouseListener(new handler());
btnConnect.addActionListener(new handler(getName()));
frame.add(fldName, BorderLayout.CENTER);
frame.setVisible(true);
}
public void clients() { //to create the chatroom GUI
frame = new JFrame("friends");
frame.setBounds(100, 100, 400, 400);
jpS2All = new JPanel();
txtChat = new JTextArea();
txtChat.setRows(25);
txtChat.setColumns(25);
txtChat.setEditable(false);
sc = new JScrollPane(txtChat);
jpS2All.add(sc);
frame.add(jpS2All, BorderLayout.WEST);
jpS2Text = new JPanel();
////////////////////////
fldText = new JTextField();
fldText.setColumns(34);
fldText.setHorizontalAlignment(JTextField.RIGHT);
fldText.addActionListener(new textSend(getName(), output));
jpS2Text.add(fldText);
frame.add(jpS2Text, BorderLayout.SOUTH);
/////////
jpS2Client = new JPanel();
listModel = new DefaultListModel();
clientList = new JList(listModel);
clientList.setFixedCellHeight(14);
clientList.setFixedCellWidth(100);
scClients = new JScrollPane(clientList);
frame.add(jpS2Client.add(scClients), BorderLayout.EAST);
/////////
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();
}//end of clients
public String getName() {
Name = fldName.getText();
return Name;
}
public void appendText(final String message) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
txtChat.append(message);
}
});
}
}
server code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
public class server {
public void start() throws IOException {
ServerSocket serverSocket = new ServerSocket(1234);
while (true) {
Socket socket = serverSocket.accept();
ClientThread t = new ClientThread(socket);
t.start();
}
}
public static void main(String[] args) throws IOException {
server server = new server();
server.start();
}
class ClientThread extends Thread {
Socket socket;
InputStream sInput;
OutputStream sOutput;
GUI gui = new GUI();
String Name;
ClientThread(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
try {
BufferedReader sInput
= new BufferedReader(new InputStreamReader(socket.getInputStream()));
Writer sOutput = new OutputStreamWriter(socket.getOutputStream());
Name = gui.getName();
gui = new GUI(Name, sOutput);
try {
String line;
while ((line = sInput.readLine()) != null) {
gui.appendText(line);
}
} catch (IOException ex) {
Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (IOException e) {
}
}
}
}
client side:
import java.net.*;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class client {
private Socket s;
private String Name;
private GUI gui;
private Writer output;
private BufferedReader input;
public void start() {
try {
s = new Socket("127.0.0.1", 1234);
} catch (IOException ex) {
}
try {
input = new BufferedReader(new InputStreamReader(s.getInputStream()));
output = new OutputStreamWriter(s.getOutputStream());
} catch (IOException eIO) {
}
Name = gui.getName();
new GUI(Name, output);
new ListenFromServer().start();
}
public static void main(String[] args) {
client cl = new client();
GUI gui = new GUI();
gui.creatServer();
}
class ListenFromServer extends Thread {
public void run() {
try {
String line;
while ((line = input.readLine()) != null) {
gui.appendText(line);
}
} catch (IOException ex) {
Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
I know my question is a bit cumbersome but I really appreciate to help me handle this issue!

I am looking at your code and it is obvious that output is null when you attempt output.write(line + "\n"); Therefore I went and looked for the possible path of execution that could leave output un-initialized. This is where debugging comes in very handy.
Here is your execution path:
In your main method of client you create a new GUI and then call gui.creatServer();
public static void main(String[] args) {
client cl = new client();
GUI gui = new GUI();
gui.creatServer();
}
output has not been assigned and is still null
in the creatServer(); method you have this line:
fldName.addMouseListener(new handler());
which the actionPerformed method of handler calls the clients(); method which has the line:
fldText.addActionListener(new textSend(getName(), output));
note output is still null
(now your textSend class doesn't even do anything inside the constructor but that aside even if it did you are still using the output variable from the GUI class)
you have the actionPerformed method in the textSend class that has the line:
output.write(line + "\n");
Without ever initializing output it is still null, which gives you the NullPointer
Please see What is a NullPointerException, and how do I fix it? as #markspace linked in the comments. In addition you should really learn how to debug small programs.
See the following links:
http://ericlippert.com/2014/03/05/how-to-debug-small-programs/
http://blogs.msdn.com/b/smallbasic/archive/2012/10/09/how-to-debug-small-basic-programs.aspx
Again in addition, consider using Anonymous classes to ease up on those lines of code, which also makes the code easier to debug and more readable.
One last thought, remember to use standard Naming Conventions for the language you are using. your code currently has a lot of incorrect lowercase classes and some uppercase methods/properties.

the error message shows that one of the variable used in the expression was null. This may be either output or line.
As chancea already mentioned, you are calling the GUI() constructor with no arguments, so the output field is not initialized. You should remove the constructor with no arguments when you have no way to initialize the output field in it and only leave the one with arguments.

Related

How does the client wait for a server response

I have a simple server to client program that lets the server send text to the clients, There is no connection problems. However, I'm not sure what to put the while loop which updates the JLabel of the sent text. If I put while(true) I get an error saying no lines found.
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Client {
private static JLabel l = new JLabel();
public Client() {
JFrame f = new JFrame("Client");
JPanel p = new JPanel(null);
f.setSize(300, 150);
f.setLocationRelativeTo(null);
l.setSize(300, 20);
l.setLocation(0, 65);
p.add(l);
f.add(p);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) throws IOException {
new Client();
Socket socket = new Socket("localhost", 12000);
Scanner in = new Scanner(socket.getInputStream());
while(/* code goes here */) {
l.setText(in.nextLine());
}
}
}
private class PollingThread implements Runnable {
public void run() {
while (true) {
try {
l.setText(in.nextLine());
} catch (/* */) {
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.d(TAG, "Thread interrupted");
}
}
}
Create a private nested class inside Client class.
Initiate the thread from Client class's main() method.
PollingThread mPollingThread = new PollingThread();
mPollingThread.start();

How to use method from another class without having the class

I have a method showMessage() that appends a string onto a JTextArea and i want to call it in my "class in the class" (ServerThread). How can i accomplish this without having Main main; or Main main = new Main();
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
private JTextArea chatWindow;
private List<Integer> ports = new ArrayList<Integer>();
public Main() throws IOException {
super("ServerConsole");
chatWindow = new JTextArea();
chatWindow.setEditable(false);
JScrollPane scrollPane = new JScrollPane(chatWindow);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setBounds(0, 20, 596, 200);
add(scrollPane);
setLayout(null);
setSize(600, 300);
setResizable(false);
setVisible(true);
getContentPane().setBackground(Color.white);
Socket s = null;
ServerSocket ss2 = null;
showMessage("Server Listening......\n");
try {
ss2 = new ServerSocket(3175);
} catch (IOException e) {
e.printStackTrace();
showMessage("Server error");
}
while (true) {
try {
s = ss2.accept();
showMessage("connection Established\n");
ports.add(s.getPort());
ServerThread st = new ServerThread(s);
st.start();
}
catch (Exception e) {
e.printStackTrace();
showMessage("Connection Error");
}
}
}
public void showMessage(final String m) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
chatWindow.append(m);
}
});
}
}
class ServerThread extends Thread {
private ObjectOutputStream output;
private ObjectInputStream input;
Socket s = null;
private static LinkedHashMap<Integer, String> playerCoords = new LinkedHashMap<Integer, String>();
public ServerThread(Socket s) {
this.s = s;
}
public void run() {
}
}
Example: in the run method i want to have something like main.showMessage(string) without having a Main object declared.
Just declare your method as static
public static void showMessage(final String m)
That way, you can call it like this -
Main.showMessage("Some String");
declare both showMessage method and chatWindow field static. Then you can call as Main.showMessage("whatever") where you want.
But a more elegant solution will be communicating these two singletons over listeners.
Declare an interface MessageListener with a method say onMessage(String message)
interface MessageListener {
public void onMessage(String message);
}
in ServerThread keep a list of MessageListeners and in run method invoke them
class ServerThread extends Thread {
// class content
static List<MessageListener> messageListeners = new ArrayList<>();
public void run() {
for (MessageListener messageListener : messageListeners) {
messageListener.onMessage("the message");
}
}
}
then make your Main class implementing MessageListener and in onMessage method call showMessage, also do not forget to register ServerThreads listener registry
public class Main extends JFrame implements MessageListener {
// class content
public Main() throws IOException {
super("ServerConsole");
ServerThread.messageListeners.add(this);
// other content
}
// class content
#Override
public void onMessage(String message) {
showMessage(message);
}
}
that's all. then the whole code looks like:
import java.awt.Color;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
public class Main extends JFrame implements MessageListener {
private static final long serialVersionUID = 1L;
private JTextArea chatWindow;
private List<Integer> ports = new ArrayList<Integer>();
public Main() throws IOException {
super("ServerConsole");
ServerThread.messageListeners.add(this);
chatWindow = new JTextArea();
chatWindow.setEditable(false);
JScrollPane scrollPane = new JScrollPane(chatWindow);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setBounds(0, 20, 596, 200);
add(scrollPane);
setLayout(null);
setSize(600, 300);
setResizable(false);
setVisible(true);
getContentPane().setBackground(Color.white);
Socket s = null;
ServerSocket ss2 = null;
showMessage("Server Listening......\n");
try {
ss2 = new ServerSocket(3175);
} catch (IOException e) {
e.printStackTrace();
showMessage("Server error");
}
while (true) {
try {
s = ss2.accept();
showMessage("connection Established\n");
ports.add(s.getPort());
ServerThread st = new ServerThread(s);
st.start();
}
catch (Exception e) {
e.printStackTrace();
showMessage("Connection Error");
}
}
}
public void showMessage(final String m) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
chatWindow.append(m);
}
});
}
#Override
public void onMessage(String message) {
showMessage(message);
}
}
interface MessageListener {
public void onMessage(String message);
}
class ServerThread extends Thread {
private ObjectOutputStream output;
private ObjectInputStream input;
Socket s = null;
private static LinkedHashMap<Integer, String> playerCoords = new LinkedHashMap<Integer, String>();
static List<MessageListener> messageListeners = new ArrayList<>();
public ServerThread(Socket s) {
this.s = s;
}
public void run() {
for (MessageListener messageListener : messageListeners) {
messageListener.onMessage("the message");
}
}
}

Understanding command line arguments

Basically I am just writing a socket.
For some reason I keep getting this error though
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at Main.ChatClient.main(ChatClient.java:143)
which is line " String server = args[0]; "
What does the args need to be to fix this issue?
package Main;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Observable;
import java.util.Observer;
// Class to manage Client chat Box.
public class ChatClient {
Main a = new Main();
/** Chat client access */
static class ChatAccess extends Observable {
private Socket socket;
private OutputStream outputStream;
#Override
public void notifyObservers(Object arg) {
super.setChanged();
super.notifyObservers(arg);
}
/** Create socket, and receiving thread */
public void InitSocket(String server, int port) throws IOException {
socket = new Socket(server, port);
outputStream = socket.getOutputStream();
Thread receivingThread = new Thread() {
#Override
public void run() {
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
notifyObservers(line);
} catch (IOException ex) {
notifyObservers(ex);
}
}
};
receivingThread.start();
}
private static final String CRLF = "\r\n"; // newline
/** Send a line of text */
public void send(String text) {
try {
outputStream.write((text + CRLF).getBytes());
outputStream.flush();
} catch (IOException ex) {
notifyObservers(ex);
}
}
/** Close the socket */
public void close() {
try {
socket.close();
} catch (IOException ex) {
notifyObservers(ex);
}
}
}
/** Chat client UI */
static class ChatFrame extends JFrame implements Observer {
private JTextArea textArea;
private JTextField inputTextField;
private JButton sendButton;
private ChatAccess chatAccess;
public ChatFrame(ChatAccess chatAccess) {
this.chatAccess = chatAccess;
chatAccess.addObserver(this);
buildGUI();
}
/** Builds the user interface */
private void buildGUI() {
textArea = new JTextArea(20, 50);
textArea.setEditable(false);
textArea.setLineWrap(true);
add(new JScrollPane(textArea), BorderLayout.CENTER);
Box box = Box.createHorizontalBox();
add(box, BorderLayout.SOUTH);
inputTextField = new JTextField();
sendButton = new JButton("Send");
box.add(inputTextField);
box.add(sendButton);
// Action for the inputTextField and the goButton
ActionListener sendListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String str = inputTextField.getText();
if (str != null && str.trim().length() > 0)
chatAccess.send(str);
inputTextField.selectAll();
inputTextField.requestFocus();
inputTextField.setText("");
}
};
inputTextField.addActionListener(sendListener);
sendButton.addActionListener(sendListener);
this.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
chatAccess.close();
}
});
}
/** Updates the UI depending on the Object argument */
public void update(Observable o, Object arg) {
final Object finalArg = arg;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textArea.append(finalArg.toString());
textArea.append("\n");
}
});
}
}
public static void main(String[] args) {
System.out.println("troll");
String server = args[0];
System.out.println("reached here");
int port =2222;
ChatAccess access = new ChatAccess();
JFrame frame = new ChatFrame(access);
frame.setTitle("MyChatApp - connected to " + server + ":" + port);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
try {
access.InitSocket(server,port);
} catch (IOException ex) {
System.out.println("Cannot connect to " + server + ":" + port);
ex.printStackTrace();
System.exit(0);
}
}
}
Given:
String server = args[0];
I'd suggest you provide the server name as your first argument to your program
With
public static void main(String[] args)
args is the array of command line args passed in.

SwingWorker and command line process interrupts

I am trying to build a Swing solution for compressing files which is relayed on the rar command line. As the GUI needs to stay responsive I've wrapped the code for dealing with the command line into a SwingWorker class.
SwingWorker<Boolean, String> worker = new SwingWorker<Boolean, String>(){
protected Boolean doInBackground() throws Exception {
Runtime rt = Runtime.getRuntime();
try {
//daj processu da ode u background nekako, da ga ne sjebem sa ctrl + c (winrar umesto rar)
String command = "my command, this works just fine";
Process p = rt.exec(command, null, new File("C:\\Program Files\\WinRar"));
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
String s = null;
System.out.println("<INPUT>");
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
System.out.println("</INPUT>");
InputStream stderr = p.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
System.out.println("<ERROR>");
String line = null;
while ( (line = br.readLine()) != null){
System.out.println(line);
return false;
}
System.out.println("</ERROR>");
int exitVal = p.waitFor();
//EXIT VALUE IS ALWAYS 0, EVEN IF I INTERRUPT IT WITH CTRL+C
System.out.println("Process exitValue: " + exitVal);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} catch (InterruptedException e) {
e.printStackTrace();
return false;
} catch (Exception e) {
return false;
}
return true;
}
#Override
protected void process(List<String> chunks) {
// TODO Auto-generated method stub
//SOME GUI UPDATES
}
#Override
protected void done() {
// TODO Auto-generated method stub
Boolean status = false;
try {
status = get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//MORE GUI UPDATES
if(status){
tableList.setValueAt("Done", row, 3);
} else{
tableList.setValueAt("Error", row, 3);
}
super.done();
}
};
worker.execute();
When I delete printing of input and error, exit value is printed as soon as rar appears on the screen. So there is no actual point of "waitFor()" method in my code. What I need is to check if rar closed without interrupts (like CTRL + C, or hitting "X" on cmd window) and get the exit code. I've tried adding shutdown hook on runtime (rt variable) but it reacts when I close the whole GUI.
You need to get your input and error streams, and read from them each in its own thread. Right now your error stream never has a chance because of the blocking while loop ahead of it.
I've used the following code (although it is years old...):
Enum: GobblerType.java
enum GobblerType {
ERROR, OUTPUT
}
Class StreamGobbler.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
public class StreamGobbler implements Runnable {
private InputStream is;
private GobblerType type;
private OutputStream os;
public StreamGobbler(InputStream is, GobblerType type) {
this(is, type, null);
}
public StreamGobbler(InputStream is, GobblerType type, OutputStream redirect) {
this.is = is;
this.type = type;
this.os = redirect;
}
public void run() {
try {
PrintWriter pw = null;
if (os != null) {
pw = new PrintWriter(os, true);
}
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
if (pw != null) {
pw.println(line);
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
And then have used it like so:
Process proc = Runtime.getRuntime().exec(.....); // TODO: Fix!
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), GobblerType.ERROR);
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), GobblerType.OUTPUT);
new Thread(errorGobbler).start();
new Thread(outputGobbler).start();
int exitVal = proc.waitFor();
proc.destroy();
OK, I created some code as a proof of concept program. I've modified my Gobbler a bit so that it doesn't require an OutputStream but rather uses a PropertyChangeListener to notify listeners of any text coming from the InputStream. For this to work, all my code is in the same package, and note that package names are key, and you would likely need to change yours. Running this code does behave as expected. It is a bit overly simplistic and probably should use some type of blocking queue for passing information between classes.
GobblerType.java
An enum to distinguish the two type of stream gobblers in use
package pkg2;
public enum GobblerType {
ERROR, OUTPUT
}
StreamGobbler2.java
The stream gobbler that uses an input stream reader to get text from the input stream, puts the text into a text field, and notifies listeners of new text. It uses a PropertyChangeListener for the notification. This is a crude way to producer-consumer, and risks not capturing all passed information. Better would be to use a blocking queue of some sort.
package pkg2;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.Callable;
public class StreamGobbler2 implements Callable<Void> {
private PropertyChangeSupport support = new PropertyChangeSupport(this);
private InputStream is;
private GobblerType type;
private String text;
public StreamGobbler2(InputStream is, GobblerType type) {
this.is = is;
this.type = type;
}
#Override
public Void call() throws Exception {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
setText(line);
}
return null;
}
public GobblerType getType() {
return type;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
support.addPropertyChangeListener(propertyName, listener);
}
public void setText(String text) {
String oldValue = null;
String newValue = text;
this.text = text;
support.firePropertyChange(type.toString(), oldValue, newValue);
}
public String getText() {
return text;
}
}
ProcessLauncher.java
This is a non-Swing class that captures the information from the two gobblers. Again, better would be to use blocking queues (next iteration)
package pkg2;
import java.beans.PropertyChangeListener;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ProcessLauncher implements Callable<Integer> {
private ExecutorService execService = Executors.newFixedThreadPool(2);
private List<String> commands;
private List<PropertyChangeListener> listeners = new ArrayList<>();
public ProcessLauncher(List<String> commands) {
this.commands = commands;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
listeners.add(listener);
}
#Override
public Integer call() throws Exception {
ProcessBuilder pb = new ProcessBuilder(commands);
Process p = pb.start();
int exitValue = 0;
try (InputStream inputStream = p.getInputStream();
InputStream errorStream = p.getErrorStream()) {
StreamGobbler2 errorGobbler = new StreamGobbler2(inputStream, GobblerType.OUTPUT);
StreamGobbler2 outputGobbler = new StreamGobbler2(errorStream, GobblerType.ERROR);
for (PropertyChangeListener listener : listeners) {
errorGobbler.addPropertyChangeListener(listener);
outputGobbler.addPropertyChangeListener(listener);
}
List<Future<Void>> futures = new ArrayList<>();
futures.add(execService.submit(errorGobbler));
futures.add(execService.submit(outputGobbler));
execService.shutdown();
exitValue = p.waitFor();
for (Future<Void> future : futures) {
future.get();
}
}
return exitValue;
}
}
SwingWorkerWrapper.java
Wrapper to use the above class in a Swing fashion
package pkg2;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.SwingWorker;
public class SwingWorkerWrapper extends SwingWorker<Integer, Void> {
private ProcessLauncher processLauncher;
public SwingWorkerWrapper(List<String> commands) {
processLauncher = new ProcessLauncher(commands);
processLauncher.addPropertyChangeListener(new LauncherListener());
}
#Override
protected Integer doInBackground() throws Exception {
return processLauncher.call();
}
private class LauncherListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
}
}
MainGui.java
GUI class that uses the above SwingWorker. Run this class to get the whole show on the road. Once running, press the "Launch Process" button for this program to run the TestProgram in a separate JVM.
package pkg2;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import javax.swing.border.Border;
#SuppressWarnings("serial")
public class MainGui extends JPanel {
private static final String[] CMD_TEXT = {"java", "-cp"};
private static final String TEST_PROGRAM = "pkg2.TestProgram";
private JTextArea inputTextArea = new JTextArea(15, 30);
private JTextArea errorTextArea = new JTextArea(15, 30);
private List<String> commands = new ArrayList<>();
public MainGui() {
for (String cmd : CMD_TEXT) {
commands.add(cmd);
}
String classpath = System.getProperty("java.class.path");
commands.add(classpath);
commands.add(TEST_PROGRAM);
inputTextArea.setFocusable(false);
JScrollPane inputScrollPane = new JScrollPane(inputTextArea);
inputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
Border outsideBorder = BorderFactory.createTitledBorder("Input Messages");
Border border = BorderFactory.createCompoundBorder(outsideBorder, inputScrollPane.getBorder());
inputScrollPane.setBorder(border);
errorTextArea.setFocusable(false);
JScrollPane errorScrollPane = new JScrollPane(errorTextArea);
errorScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
outsideBorder = BorderFactory.createTitledBorder("Error Messages");
border = BorderFactory.createCompoundBorder(outsideBorder, errorScrollPane.getBorder());
errorScrollPane.setBorder(border);
JPanel twoAreasPanel = new JPanel(new GridLayout(1, 0, 3, 3));
twoAreasPanel.add(inputScrollPane);
twoAreasPanel.add(errorScrollPane);
JPanel btnPanel = new JPanel(new GridLayout(1, 0, 3, 3));
btnPanel.add(new JButton(new LaunchProcessAction()));
btnPanel.add(new JButton(new ExitAction()));
setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
setLayout(new BorderLayout(3, 3));
add(twoAreasPanel, BorderLayout.CENTER);
add(btnPanel, BorderLayout.PAGE_END);
}
private class SwWrapperListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
SwingWorkerWrapper swW = (SwingWorkerWrapper) evt.getSource();
try {
int exitCode = swW.get();
inputTextArea.append("Exit Code: " + exitCode + "\n");
} catch (InterruptedException e) {
e.printStackTrace();
inputTextArea.append(e.getLocalizedMessage());
inputTextArea.append("\n");
} catch (ExecutionException e) {
e.printStackTrace();
inputTextArea.append(e.getLocalizedMessage());
inputTextArea.append("\n");
}
} else if (GobblerType.OUTPUT.toString().equals(evt.getPropertyName())) {
inputTextArea.append(evt.getNewValue() + "\n");
} else if (GobblerType.ERROR.toString().equals(evt.getPropertyName())) {
errorTextArea.append(evt.getNewValue() + "\n");
}
}
}
private class LaunchProcessAction extends MyAction {
public LaunchProcessAction() {
super("Launch Process", KeyEvent.VK_L);
}
#Override
public void actionPerformed(ActionEvent e) {
SwingWorkerWrapper swWrapper = new SwingWorkerWrapper(commands);
swWrapper.addPropertyChangeListener(new SwWrapperListener());
swWrapper.execute();
}
}
private class ExitAction extends MyAction {
public ExitAction() {
super("Exit", KeyEvent.VK_X);
}
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
private static abstract class MyAction extends AbstractAction {
public MyAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
}
private static void createAndShowGui() {
MainGui mainPanel = new MainGui();
JFrame frame = new JFrame("Main GUI");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
TestProgram.java
Don't run this program directly, but rather have the Main GUI run this. Be sure that this code and all the code is compiled however
package pkg2;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class TestProgram extends JPanel {
private JTextField textField = new JTextField(20);
private JSpinner exitCodeSpinner = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1));
public TestProgram() {
SendTextAction sendTextAxn = new SendTextAction();
textField.setAction(sendTextAxn);
JPanel panel1 = new JPanel();
panel1.add(textField);
panel1.add(new JButton(sendTextAxn));
JPanel panel2 = new JPanel();
panel2.add(new JLabel("Exit Code:"));
panel2.add(exitCodeSpinner);
panel2.add(new JButton(new ExitCodeAction()));
panel2.add(new JButton(new ThrowExceptionAction()));
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(panel1);
add(panel2);
}
private static abstract class MyAction extends AbstractAction {
public MyAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
}
private class SendTextAction extends MyAction {
public SendTextAction() {
super("Send Text", KeyEvent.VK_S);
}
#Override
public void actionPerformed(ActionEvent e) {
String text = textField.getText();
textField.setText("");
System.out.println(text);
}
}
private class ExitCodeAction extends MyAction {
public ExitCodeAction() {
super("Exit Code", KeyEvent.VK_X);
}
#Override
public void actionPerformed(ActionEvent e) {
int exitCode = (int) exitCodeSpinner.getValue();
System.exit(exitCode);
}
}
private class ThrowExceptionAction extends MyAction {
public ThrowExceptionAction() {
super("Throw Exception", KeyEvent.VK_T);
}
#Override
public void actionPerformed(ActionEvent e) {
// throw some unchecked exception
throw new NumberFormatException("Unchecked exception thrown from within TestProgram");
}
}
private static void createAndShowGui() {
TestProgram mainPanel = new TestProgram();
JFrame frame = new JFrame("Test Program");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

Content in JFrame doesn't show when called from a specific method

The content in the JFrame WaitingFrame doesn't show up as expected. This is in essence what I am trying to do:
package org.brbcoffee.missinggui;
import java.net.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main {
public static KeynoteServer server;
public static void main(String[] args){
JFrame frame = new JFrame("SSCCE");
JButton btn = new JButton("Test");
btn.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
connectToPhone();
}
});
frame.add(btn);
frame.pack();
frame.setVisible(true);
}
public static void connectToPhone(){
WaitingFrame wf = new WaitingFrame();
wf.setVisible(true);
server = new KeynoteServer();
if (server.setup()){
System.out.println("Server set up and connected");
} else {
System.out.println("Couldn't connect");
}
wf.dispose();
}
}
#SuppressWarnings("serial")
class WaitingFrame extends JFrame {
public WaitingFrame(){
setTitle("Waiting");
this.setLocationByPlatform(true);
JLabel label = new JLabel("Waiting for client..."); // This will never show
JPanel content = new JPanel();
content.add(label);
this.add(content);
pack();
}
}
class KeynoteServer{
private int port = 55555;
private ServerSocket server;
private Socket clientSocket;
public boolean setup() {
try {
server = new ServerSocket(55555);
server.setSoTimeout(10000);
clientSocket = server.accept();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}
When setup() is called the WaitingFrame does indeed show up, but the contents are missing. I've tried different image formats, but it does work from other methods and classes, so that shouldn't matter. Does anyone know what's going on here?
Use SwingUtilities.invokeLater()/invokeAndWait() to show your frame because all the GUI should be updated from EDT.

Categories