Recently I was doing multithreaded chat application. Now I am struggling with server. I am trying to stop the server by introducing new field online, but it doesn't help.
import view.ChatHub;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ChatServer extends Thread {
// All client names, so we can check for duplicates upon registration.
private static Set<String> names = new HashSet<>();
// The set of all the print writers for all the clients, used for broadcast.
private static Set<PrintWriter> writers = new HashSet<>();
private ChatHub frame;
private int port;
private boolean online;
private ExecutorService pool;
public ChatServer(int port) throws IOException {
System.out.println("The chat server is running...");
this.frame = new ChatHub(this);
this.port = port;
this.online = true;
this.pool = Executors.newFixedThreadPool(500);
this.start();
}
#Override
public void run() {
while (this.online) {
try (ServerSocket listener = new ServerSocket(this.port)) {
this.pool.execute(new Handler(listener.accept(), this.names, this.writers));
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void stopChatServer() {
this.pool.shutdown();
this.online = false;
}
public Set<String> getNames() {
return this.names;
}
public Set<PrintWriter> getWriters() {
return this.getWriters();
}
public ChatHub getFrame() {
return this.frame;
}
public int getPort() {
return this.port;
}
}
Here I am trying to close the server:
import Client.ChatClient;
import Server.ChatServer;
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;
public class ChatHub extends JFrame{
JTextField textField;
JTextArea messageArea;
public ChatHub(ChatServer server) {
new JFrame("Chatter");
this.textField = new JTextField(50);
this.messageArea = new JTextArea(16,50);
this.textField.setEditable(true);
this.messageArea.setEditable(false);
this.getContentPane().add(this.textField, BorderLayout.SOUTH);
this.getContentPane().add(new JScrollPane(this.messageArea), BorderLayout.CENTER);
this.pack();
// this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
server.stopChatServer();
}
});
this.setVisible(true);
}
public void appendMessage(String line) {
messageArea.append(line + "\n");
}
public JTextField getTextField() {
return this.textField;
}
public JTextArea getMessageArea() {
return this.messageArea;
}
public void nameAccepted(String line) {
this.setTitle("Chatter - " + line.substring(13));
textField.setEditable(true);
}
}
Also I tried to have method run with while just printing some String. But when it left run method the program was still working. Any suggestions? Thanks in advance.
Also I tried to implement run() as the following:
#Override
public void run() {
while (this.online) {
System.out.println("Some String");
}
System.out.println(this.isAlive() + "\n");
}
And it does output true in the end.
Maybe you have another problem here (Swing Related):
Your new JFrame("Chatter") just creates a new JFrame and does nothing with it. You have to call super("Chatter"); to call the super constructor
Try
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
Have you tried to declare your online property as volatile?
private volatile boolean online = true;
If you do not declare a property as volatile, the JIT compiler can assume that your boolean property will not be changed by another thread. So it may optimize your run method to
public void run() {
if (!online)
return;
while(true) {
try(/*...*/) {
// ...
} catch(/*...*/) {
// ...
}
}
}
Related
I have a problem with my Observer pattern when I'm trying to update the client view based on the data that comes from the server.
The code that sends messages to the client:
package model;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class PlayerThread extends Thread{
private BufferedReader inFromClient;
private PrintWriter outToClient;
private Socket socket;
public PlayerThread(Socket socket) throws IOException{
this.socket = socket;
inFromClient = new BufferedReader(new InputStreamReader(socket.getInputStream()));
outToClient = new PrintWriter(socket.getOutputStream(), true);
outToClient.println("Hey");
}
public void run(){
// needs to be implemented
}
}
The code that takes care of recieving data on the client side:
package connection;
import java.io.BufferedReader;
import mediator.ModelManager;
public class ClientRecieverThread extends Thread {
private ModelManager model;
private BufferedReader inFromServer;
public ClientRecieverThread(ModelManager model, BufferedReader inFromServer) {
this.model = model;
this.inFromServer = inFromServer;
}
#Override
public void run() {
String message = null;
try {
while (true) {
message = inFromServer.readLine();
//System.out.println(message);
model.setMessage(message);
}
} catch (Exception e) {
}
}
}
and the code that contains the observer pattern that I use on the client side:
package model;
public class MessageHandler {
private String message;
public MessageHandler(){
message = "Welcome";
}
public void setMessage(String message){
this.message = message;
}
public String getMessage(){
return this.message;
}
}
package mediator;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Observable;
import model.MessageHandler;
import connection.Proxy;
public class ModelManager extends Observable{
private MessageHandler messageHandler;
private Proxy proxy; // establish the connection with the server
public ModelManager() throws UnknownHostException, IOException{
proxy = new Proxy(this);
messageHandler = new MessageHandler();
}
public synchronized void setMessage(String message){
setChanged();
messageHandler.setMessage(message);
notifyObservers(message);
}
public String getMessage(){
return messageHandler.getMessage();
}
public void send(String message){
proxy.send(message);
}
}
package view;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import model.Board;
public class View extends JPanel implements Observer{
private JFrame frame;
private Board board;
private Rectangle[][] squares;
private JLabel messageLabel;
public View(){
initComponents();
initVariables();
addComponents();
}
private void initComponents() {
frame = new JFrame("TicTacToe");
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
messageLabel = new JLabel("");
messageLabel.setHorizontalAlignment(SwingConstants.CENTER);
messageLabel.setVerticalAlignment(SwingConstants.CENTER);
this.setSize(300,300);
}
private void initVariables() {
board = new Board(3,3, 100);
squares = new Rectangle[board.getRow()][board.getCol()];
for (int i = 0; i < squares.length; i++){
for (int j = 0; j < squares[i].length; j++){
squares[i][j] = new Rectangle();
}
}
}
private void addComponents() {
frame.add(messageLabel, BorderLayout.NORTH);
frame.add(this);
frame.setVisible(true);
}
private void drawBoard(Graphics g, Board board) {
int positionX = this.getWidth()/2 - (board.getCellSize() + board.getCellSize()/2);
int positionY = 50;
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < board.getRow(); i++) {
for (int j = 0; j < board.getCol(); j++) {
squares[i][j] = new Rectangle(positionX, positionY, 100, 100);
positionX += 100;
g2d.draw(squares[i][j]);
}
positionY += board.getCellSize();
positionX = this.getWidth()/2 - (board.getCellSize() + board.getCellSize()/2);
}
}
public void paintComponent(Graphics g){
drawBoard(g, board);
}
public void update(Observable o, Object arg) {
System.out.println((String)arg);
}
}
The connection between the ModelManager and the View is done in the Controller class like this (model.addObserver(view));
The prolem is that I don't understand why the view doesn't get updated, although the data gets recieved by the client.
Thank you in advance! :)
You need to call the method notifyObservers(); when the model is changed, this is will notify the observers added to the model.
So inside the setMessage(String message), do the coding:
if(this.message!=message){
this.message = message;
notifyObservers();
}
I’ve written two java desktop applications. One of them(I call it launcher) starts another(client) application.
Both applications were compiled under java 1.6.0_32 [oracle jvm, hot spot].
The Exception handler was specified in the main thread of client app.
When the launcher is started under java 1.6.0_32 everything is OK,
But when it started under 1.7x_xx and later, the exception handler begin work unproperly.
It does not catch any exceptions.
I have write the test example, the source code was listed
The launcher app source:
Start.java:
import javax.swing.*;
import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
public class Start {
private ClassLoader classLoader;
private List<URL> urls = new ArrayList<URL>();
private LauncherFrame launcherFrame = new LauncherFrame();
private Start() {
urls = generateUrls();
}
public static void main(String[] args) {
new Start().initClassLoader(args);
}
public void initClassLoader(String[] args) {
launcherFrame.initComponents();
this.classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), Start.class.getClassLoader());
try {
Class<?> mainClass = classLoader.loadClass("my.example.Runner");
Object instance = mainClass.newInstance();
Method runMethod = mainClass.getMethod("run", null);
runMethod.invoke(instance);
} catch (Exception e) {
System.out.println("exception: ");
e.printStackTrace();
}
}
public List<URL> generateUrls() {
File jarFile = new File("prog2.jar");
List<URL> urls = new ArrayList<URL>();
if (jarFile.exists()) {
try {
urls.add(jarFile.toURI().toURL());
} catch (MalformedURLException e) {
}
}
return urls;
}
class LauncherFrame extends JFrame {
public void initComponents() {
// if i comment next line, everything works fine in java 6 and 7
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
}
}
}
The client app source(prog2.jar):
Runner.java:
package my.example;
public class Runner {
public static void main(String args[]) {
new Runner().run();
}
public void run() {
ThreadGroup exceptionHandler = new ThreadGroup("Global Exception Handler") {
#Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("UncaughtException method called");
e.printStackTrace();
}
};
Runnable runnable = new MainThread();
Thread thread = new Thread(exceptionHandler, runnable);
thread.start();
}
}
MainThread.java:
package my.example;
import java.awt.*;
public class MainThread implements Runnable {
public void run() {
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new MyQueue());
MainFrame w = new MainFrame();
w.open();
}
public class MyQueue extends EventQueue {
protected void dispatchEvent(AWTEvent event) {
super.dispatchEvent(event);
}
}
}
MainFrame.java:
package my.example;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
public class MainFrame extends WindowAdapter {
private JFrame frame;
public MainFrame() {
frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(new Dimension(200, 200));
JPanel panel = new JPanel();
JButton button = new JButton("BREAK THIS");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
breakThis();
}
});
panel.add(button);
frame.setContentPane(panel);
}
public void breakThis() {
throw new RuntimeException("EXCEPTION OF CLICK");
}
public void open() {
this.frame.setVisible(true);
}
}
Anyone can tell me why TryGraphic freeze the JFrame with a scanner in the first main()? If I remove the Scanner or if I execute the code directly, all works. (The original "Try" class obviously do a lot of different stuff. I wrote these classes to make it simple.)
import java.util.Scanner;
public class Try {
public Try(){
}
public static void main(String[] args){
System.out.println("FOO");
String s = new Scanner(System.in).nextLine();
System.out.println(s);
}
}
This is the graphic implementation:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Scanner;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
public class TryGraphic extends JFrame{
/**
*
*/
private static final long serialVersionUID = 7491282237007954227L;
private JButton execute = new JButton("Esegui");
private PipedInputStream inPipe = new PipedInputStream();
private PipedInputStream outPipe = new PipedInputStream();
private JTextField tfIn = new JTextField();
private JTextArea outputArea = new JTextArea();
private PrintWriter inWriter;
public TryGraphic(){
super("TRY");
System.setIn(inPipe);
try {
System.setOut(new PrintStream(new PipedOutputStream(outPipe), true));
inWriter = new PrintWriter(new PipedOutputStream(inPipe), true);
}catch (IOException ioe){
ioe.printStackTrace();
}
tfIn.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent event){
String text = tfIn.getText();
tfIn.setText("");
inWriter.println(text);
}
});
this.add(execute,BorderLayout.SOUTH);
this.add(new JScrollPane(outputArea),BorderLayout.CENTER);
this.add(tfIn, BorderLayout.NORTH);
execute.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
SwingWorker<Void,String> worker = new SwingWorker<Void, String>() {
protected Void doInBackground() throws Exception {
Scanner s = new Scanner(outPipe);
while (s.hasNextLine()) {
String line = s.nextLine();
publish(line);
}
return null;
}
#Override
protected void process(java.util.List<String> chunks) {
for (String line : chunks){
outputArea.append(line+System.lineSeparator());
outputArea.validate();
}
}
};
worker.execute();
Try.main(new String[]{""});
}
});
this.setSize(300,300);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args){
new TryGraphic();
}
}
You're blocking the GUI Event Dispatch Thread. You need to spawn a separate thread to wait for input so you can keep your GUI responsive.
You're already doing the right thing by creating a SwingWorker to handle I/O in your TryGraphic class. You should do something similar to move the Try.main(new String[]{""}); call off the Event Dispatch Thread, which will keep your JFrame from locking up.
I am relatively new to Java and I am trying to make a class called VisualObject whose instances can be dragged and dropped between JPanels.
This is the getTransferData function for VisualObject, which implements Transferable:
public Object getTransferData(DataFlavor d)
throws UnsupportedFlavorException, IOException {
if (d != visualObjectDataFlavor) {
throw new UnsupportedFlavorException(d);
}
return this;
}
However, in the TransferHandler for my JPanels I run this line
System.out.println(t.getTransferData(VisualObject.visualObjectDataFlavor).equals(t.getTransferData(VisualObject.visualObjectDataFlavor)));
And it always returns false. How can I actually return a consistent pointer to the object that is being transferred? Or is there a better way to transfer it?
Second Edit:
My original code is still below, but the problem still occurs in this much simplified code:
package trytwo;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
public class SimplifiedVisualObject implements Transferable {
public static void main(String[] args) {
SimplifiedVisualObject a, b;
a = new SimplifiedVisualObject();
b = new SimplifiedVisualObject();
try {
System.out.println(a.getTransferData(DataFlavor.imageFlavor).equals(b.getTransferData(DataFlavor.imageFlavor)));
} catch (UnsupportedFlavorException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
return this;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return null;
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return false;
}
}
Edit:
Below are the three classes that I wrote: VisualObject (the Transferable), ObjectFrame (which holds the VisualObjects in its content pane), and Main, which just creates two ObjectFrames.
VisualObject:
package tryone;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.geom.Ellipse2D;
import java.io.IOException;
import javax.swing.JComponent;
public class VisualObject extends JComponent implements Transferable {
private static final long serialVersionUID = -7105793092357377791L;
public static DataFlavor visualObjectDataFlavor = new DataFlavor(VisualObject.class, "Visual Object");
public VisualObject() {
setOpaque(true);
setLayout(null);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
g2d.fill(new Ellipse2D.Double(0, 0,getWidth(),getHeight()));
}
#Override
public Transferable getTransferData(DataFlavor d)
throws UnsupportedFlavorException, IOException {
if (d != visualObjectDataFlavor) {
throw new UnsupportedFlavorException(d);
}
return this;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
DataFlavor d[] = {visualObjectDataFlavor};
return d;
}
#Override
public boolean isDataFlavorSupported(DataFlavor d) {
if (d == visualObjectDataFlavor) {
return false;
}
return false;
}
public VisualObject getThis() {
return this;
}
}
ObjectFrame:
package tryone;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.TransferHandler;
public class ObjectFrame extends JFrame implements DragGestureListener {
private static final long serialVersionUID = 4330669242071962048L;
protected Cursor draggingCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
private JPanel panel;
public ObjectFrame() {
setVisible(true);
setSize(300, 400);
setLayout(new BorderLayout());
setTitle("Object Tray");
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
panel = new JPanel();
panel.setBackground(Color.white);
panel.setLayout(null);
add(panel, BorderLayout.CENTER);
final DragSource ds = new DragSource();
final DragGestureListener handle = this;
panel.setTransferHandler(new ObjectTransferHandler());
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
VisualObject v = null;
v = new VisualObject();
v.setSize(50, 50);
v.setLocation(100, 100);
ds.createDefaultDragGestureRecognizer(v,
DnDConstants.ACTION_COPY, handle);
panel.add(v);
panel.repaint();
}
});
}
#Override
public void dragGestureRecognized(DragGestureEvent dge) {
if (dge.getComponent() instanceof Transferable) {
dge.startDrag(draggingCursor, (Transferable) dge.getComponent());
}
}
private class ObjectTransferHandler extends TransferHandler {
private static final long serialVersionUID = 5072686271469629699L;
public ObjectTransferHandler() {
}
public boolean canImport(TransferSupport t) {
if (t.isDataFlavorSupported(VisualObject.visualObjectDataFlavor)) {
return true;
}
return false;
}
public boolean importData(TransferSupport ts) {
Transferable t = ts.getTransferable();
try {
System.out.println(t.getTransferData(VisualObject.visualObjectDataFlavor) == (t.getTransferData(VisualObject.visualObjectDataFlavor)));
return true;
} catch (UnsupportedFlavorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
}
}
Main:
package tryone;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame f1 = new ObjectFrame();
JFrame f2 = new ObjectFrame();
}
}
Regarding SimplifiedVisualObject version: The getTransferData() method return this. Since you are creating two different instances you are effectively comparing a.equals(b) which in general is always false unless equals() is overriden.
Regarding your original question: If you debug your code you will see that this is also the case with the VisualObject. Every time you call getTransferData() a different instance is returned thus the equals() comparison returns false.
If you use DataFlavor.imageFlavor it will work as expected though I cannot explain exactly why.
Perhaps when using an unknown flavor the default drag & drop implementation does not know how to handle it. As a result the transferable object is serialized and de-serialized during the transfer resulting in always creating a different instance.
Edit: Actually the answer is given by the Java tutorial that suggests that when you know that you will only transfer objects locally (inside the same application) you should create your new DataFlavor like visualObjectDataFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=tryone.VisualObject"); (to have it as a static field as in your example use a static initializer block). This will let the system know that you are transfering a local object in the application so it will return the same instance passing your object comparison test.
I instantiated a button of my class like so:
linkBtn = new LinkButton(
new URI("http://www.example.com"),
"Click me");
Nothing happens when I click it, so I want to add an action listener something like this:
linkBtn.addActionListener(SOMETHING);
I tried things like this:
linkBtn.addActionListener(new LinkButton.OpenUrlAction());
That gives the following error:
an enclosing instance that contains LinkButton.OpenUrlAction is
required
I haven't found the right syntax yet.
Here's my class that extends JButton:
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URI;
import javax.swing.JButton;
public class LinkButton extends JButton
implements ActionListener {
/** The target or href of this link. */
private URI target;
final static private String defaultText = "<HTML>Click the <FONT color=\"#000099\"><U>link</U></FONT>"
+ " to go to the website.</HTML>";
public LinkButton(URI target, String text) {
super(text);
this.target = target;
//this.setText(text);
this.setToolTipText(target.toString());
}
public LinkButton(URI target) {
this( target, target.toString() );
}
public void actionPerformed(ActionEvent e) {
open(target);
}
class OpenUrlAction implements ActionListener {
#Override public void actionPerformed(ActionEvent e) {
open(target);
}
}
private static void open(URI uri) {
if (Desktop.isDesktopSupported()) {
try {
Desktop.getDesktop().browse(uri);
} catch (IOException e) { /* TODO: error handling */ }
} else { /* TODO: error handling */ }
}
}
I'm open to suggestions. I don't like my program structure either...
The answer's provided so far are all excellent.
Hovercraft has suggest the use of Actions, which would simply the structure of your code.
For example...
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class LinkButtonExample {
public static void main(String[] args) {
new LinkButtonExample();
}
public LinkButtonExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(new JButton(new OpenURLAction(new URL("http://stackoverflow.com/"))));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (MalformedURLException ex) {
ex.printStackTrace();
}
}
});
}
public class OpenURLAction extends AbstractAction {
private URL url;
public OpenURLAction(URL url) {
this("<HTML>Click the <FONT color=\\\"#000099\\\"><U>link</U></FONT> to go to the website.</HTML>", url);
}
public OpenURLAction(String text, URL url) {
putValue(NAME, text);
setURL(url);
}
public void setURL(URL url) {
this.url = url;
setEnabled(
url != null
&& Desktop.isDesktopSupported()
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE));
putValue(SHORT_DESCRIPTION, url == null ? null : url.toString());
}
public URL getURL() {
return url;
}
#Override
public void actionPerformed(ActionEvent e) {
if (isEnabled()) {
URL url = getURL();
if (url != null && Desktop.isDesktopSupported()
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
try {
Desktop.getDesktop().browse(url.toURI());
} catch ( IOException | URISyntaxException ex) {
ex.printStackTrace();
}
}
}
}
}
}
Check out How to use Actions for more details
You could do this:
linkBtn.addActionListener(linkBtn.new OpenUrlAction());
But your program structure makes me wince. Myself I'd try to get Actions separate from views. I also much prefer extension by composition rather than inheritance.
I don't understand why you extends a JButton but you can add by default this listener in constructor.
public LinkButton(URI target, String text) {
super(text);
this.target = target;
//this.setText(text);
this.setToolTipText(target.toString());
this.addActionListener(this);
//this.addActionListener(new OpenUrlAction());
}
Or you can do this.
linkBtn.addActionListener(linkBtn.new OpenUrlAction());
outerObject.new InnerClass()
Or you can modify your inner class with a constructor injection
class OpenUrlAction implements ActionListener{
private URI target;
OpenUrlAction(URI target){
this.target=target;
}
#Override
public void actionPerformed(ActionEvent evt){
open(this.target);
}
}
In client code:
`linkBtn.addActionListener(linkBtn.new OpenUrlAction(lninkBtn.getTarget)); // or the target that you want`
Here what I came up with so far. I added this method to my LinkButton class:
public void init() {
this.addActionListener(this);
}
Then I added this code to add the action listener:
linkBtnDonate.init();
It's working. I'm open to other suggestions.