I'm making a chat client for a assignment and I have managed to get everything working pretty well. However, I have one error and cannot find the cause for it at all. When printing a message to one of my chat windows I get a NullPointerException. The thing is, the code still does exactly what it is supposed to and its very inconsistent. I could print 5 messages and get it 5 times, or print 20 and get none.
So far I have been able to narrow down the error to one function, but have not being able to narrow it down to a single line within the function. The error message I get isn't much help either. I've tried commenting out each line of code and still can't find out where it is going wrong. I've even tried to catch the error using a try and catch and it didn't work.
Below is is the minimum code required to cause the error.
import java.awt.*;
import java.util.*;
// Import window library and listeners
import javax.swing.*;
import java.awt.event.*;
// Import time libraries
import java.util.Date;
import java.text.SimpleDateFormat;
public class ChatClientTest {
// Initialize user information
private String nickname = "testNickname";
// Track open windows
private ArrayList<JFrame> windows = new ArrayList<>();
public ChatClientTest() {
openWindow("@ChatBot");
openWindow("@someUser");
// Create thread to listen to server
Thread server = new Thread(new Runnable() {
@Override
public void run() {
// Run listenToServer in separate thread
listenToServer();
}
});
// Start server listening thread
server.start();
}
private void listenToServer() {
int i = 0;
while (true) {
for (JFrame window : windows) {
try {
Thread.sleep(2000);
printToWindow(window.getTitle(), nickname, "Test message" + i);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
}
i++;
}
}
// Opens a new chat window for a given channel or user
private void openWindow(String name) {
// Create new JFrame with a text area and scroll bar
JFrame frame = new JFrame(name);
JTextArea textArea = new JTextArea(20,60);
JScrollPane scrollBar = new JScrollPane(textArea);
JTextArea inputArea = new JTextArea("");
// Set text area properties
textArea.setMargin(new Insets(5,10,20,10));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
// Set scroll bar properties
scrollBar.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
// Set input field properties
inputArea.setMargin(new Insets(5,10,5,10));
inputArea.setLineWrap(true);
inputArea.setWrapStyleWord(true);
// Add event listener on input area to track enter being pressed
inputArea.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER){
// Stop new line
e.consume();
// Check message isn't empty
if (!inputArea.getText().equals("")) {
// Print to the window and clear the text area
printToWindow(nickname, name, inputArea.getText());
inputArea.setText("");
}
}
}
// These are required but don't do anything
@Override public void keyReleased(KeyEvent e) {}
@Override public void keyTyped(KeyEvent e) {}
});
// put things into the frame
frame.add(scrollBar, BorderLayout.CENTER);
frame.add(inputArea, BorderLayout.SOUTH);
frame.pack();
// Set properties of the frame
frame.setSize(400,500);
frame.setVisible(true);
// Print a different starting message depending on the recipient
if (name.startsWith("#")) { // Joining a channel
textArea.append("Welcome, " + nickname + ", to the " + name + " channel! Be nice, start chatting and get to know some people. Leave the channel to close this window.");
} else { // Messaging a user
textArea.append("This is the start of your messages with " + name + ". Be nice and have fun chatting.");
}
windows.add(frame);
}
// Prints messages to/from a given channel in the appropriate chat window
private void printToWindow(String sender, String recipient, String message) {
// Iterate through windows
for (JFrame window : windows) {
// Get current timestamp
String timestamp = new SimpleDateFormat("h:mm a").format(new Date()).toLowerCase();
// Check who the recipient/sender is so the appropriate window is chosen
if (recipient.equals(nickname) && window.getTitle().equalsIgnoreCase(sender)) { // User is the recipient
// Get the JFrames textArea and scrollPane
JScrollPane scrollPane = (JScrollPane) window.getContentPane().getComponent(0);
JTextArea textArea = (JTextArea) scrollPane.getViewport().getView();
// Make the textArea editable
textArea.setEditable(true);
// Append the message to the window and scroll window down
textArea.append("\n\n" + sender.replace("@", "") + " | " + timestamp + "\n" + message);
textArea.validate();
scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getMaximum());
scrollPane.validate();
// Make it non-editable again
textArea.setEditable(false);
} else if (window.getTitle().equalsIgnoreCase(recipient)) { // Any other recipient
// Get the JFrames textArea and scrollPane
JScrollPane scrollPane = (JScrollPane) window.getContentPane().getComponent(0);
JTextArea textArea = (JTextArea) scrollPane.getViewport().getView();
// Make the textArea editable
textArea.setEditable(true);
// Append the message to the window
textArea.append("\n\n" + sender.replace("@", "") + " | " + timestamp + "\n" + message);
textArea.validate();
scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getMaximum());
scrollPane.validate();
// Make it non-editable again
textArea.setEditable(false);
}
}
}
public static void main(String[] args) {
new ChatClientTest();
}
}
This is the error intellij is giving me:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.text.WrappedPlainView$WrappedLine.paint(WrappedPlainView.java:584)
at javax.swing.text.BoxView.paintChild(BoxView.java:161)
at javax.swing.text.BoxView.paint(BoxView.java:433)
at javax.swing.text.WrappedPlainView.paint(WrappedPlainView.java:369)
at javax.swing.plaf.basic.BasicTextUI$RootView.paint(BasicTextUI.java:1434)
at javax.swing.plaf.basic.BasicTextUI.paintSafely(BasicTextUI.java:737)
at javax.swing.plaf.basic.BasicTextUI.paint(BasicTextUI.java:881)
at javax.swing.plaf.basic.BasicTextUI.update(BasicTextUI.java:860)
at javax.swing.JComponent.paintComponent(JComponent.java:780)
at javax.swing.JComponent.paint(JComponent.java:1056)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JViewport.paint(JViewport.java:728)
at javax.swing.JComponent.paintChildren(JComponent.java:889)
at javax.swing.JComponent.paint(JComponent.java:1065)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5210)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)
at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
at javax.swing.JComponent._paintImmediately(JComponent.java:5158)
at javax.swing.JComponent.paintImmediately(JComponent.java:4969)
at javax.swing.RepaintManager$4.run(RepaintManager.java:831)
at javax.swing.RepaintManager$4.run(RepaintManager.java:814)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)
at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
The program is still functions exactly as expected, it just randomly gives this error.
Can someone help me find the cause of the NullPointerException? Let me know if there is anything unclear about how the program works and thank you for your help. This is my first post here so I hope I made everything clear.
Event Dispatch Thread (EDT)
or you can have random results. I'm making a chat client - so I'm guessing the chat client runs in a separate Thread, which could cause the problem. You need to use SwingUtilities.invokeLater(...) when you want to update the GUI. Read the section from the Swing tutorial on Concurrency for more information. – Markman.invokeLater()
may not be enough. Again, do consider posting a minimal reproducible example with your question. – Dowdyprivate void printToWindow(....) { SwingUtilites.invokeLater(() -> printToWindowOnEdt(...)); }
(withprintToWindowOnEdt
containing all the code that is currently in theprintToWindow
method) should solve the issue. Not very clean and elegant, but at least it should behave deterministically after that... – Tarnish