Restrict multiple instances of an application in java - java

I want to prevent multiple instances of application being launched in java. I know 2 methods for this:
locking file
locking socket
But which is one is more efficient and good to use? Which one should I use?
Any other solution to do the same are also welcome.

There is a library called jUnique which does that and will save you the bother of implementing it yourself.

If you deploy with Java WebStart the SingleInstanceService does this.
See http://download.oracle.com/javase/6/docs/technotes/guides/javaws/developersguide/faq.html#218

EDIT: I tried that with Win200864b(version isn't important) and alive JFrame and move toFront() or Iconified in SystemTray with JFrame.DO_NOTHING_ON_CLOSE
public interface ApplicationStartedListener {
void applicationStarted();
void foreignApplicationStarted(String name);
void messageArrived(Object obj);
}
//
import java.io.Serializable;
public class ClassCheck implements Serializable {
private static final long serialVersionUID = 1L;
private String className = null;
public ClassCheck() {
}
public ClassCheck(String className) {
setClassName(className);
}
#Override
public String toString() {
return this.className;
}
public String getClassName() {
return this.className;
}
public void setClassName(String className) {
this.className = className;
}
}
//
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.io.File;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class RunOnceFromFile {
private SingleInstanceController sic = null;
private JFrame frame;
private Robot r;
private JTextField tf;
public RunOnceFromFile() {
try {
r = new Robot();
} catch (AWTException ex) {
ex.printStackTrace();
}
sic = new SingleInstanceController(new File(System.getProperty("java.io.tmpdir") + "Example.file"), "sic_example_application");
if (sic.isOtherInstanceRunning()) {
sic.sendMessageToRunningApplication("toFront");
System.exit(0);
} else {
frame = new JFrame("TEST");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
tf = new JTextField("JTextFiled");
frame.add(tf, BorderLayout.NORTH);
frame.setExtendedState(Frame.ICONIFIED);
frame.setExtendedState(Frame.NORMAL);
frame.setExtendedState(frame.getExtendedState() | JFrame.ICONIFIED);
frame.setExtendedState(frame.getExtendedState() & (~JFrame.ICONIFIED));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
sic.registerApplication();
sic.addApplicationStartedListener(new ApplicationStartedListener() {
public void applicationStarted() {
Runnable doRun = new Runnable() {
public void run() {
frame.toFront();
}
};
SwingUtilities.invokeLater(doRun);
}
public void foreignApplicationStarted(final String name) {
Runnable doRun = new Runnable() {
public void run() {
frame.toFront();
}
};
SwingUtilities.invokeLater(doRun);
}
public void messageArrived(final Object obj) {
Runnable doRun = new Runnable() {//activateWindow(frame);
public void run() {
frame.toFront();
}
};
SwingUtilities.invokeLater(doRun);
}
private void activateWindow(JFrame frame) {
frame.setExtendedState(Frame.ICONIFIED);
frame.setExtendedState(Frame.NORMAL);
frame.setAlwaysOnTop(true);
frame.setAlwaysOnTop(false);
Point location = MouseInfo.getPointerInfo().getLocation();
Point locationOnScreen = frame.getLocationOnScreen();
r.mouseMove(locationOnScreen.x + 100, locationOnScreen.y + 10);
r.mousePress(InputEvent.BUTTON1_MASK);
r.mouseRelease(InputEvent.BUTTON1_MASK);
r.mouseMove(location.x, location.y);
}
});
}
}
public static void main(String[] args) {
RunOnceFromFile roff = new RunOnceFromFile();
}
}
//
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class SingleInstanceController {
private String appname = null;
private Socket client = null;
private File file = null;
private ArrayList<ApplicationStartedListener> listener = null;
private ObjectInputStream ois = null;
private ObjectOutputStream oos = null;
private boolean result = false;
private ServerSocket server = null;
public SingleInstanceController(String appname) {
this(new File(System.getProperty("java.io.tmpdir") + "/923jhakE53Kk9235b43.6m7"), appname);
}
public SingleInstanceController(File file, String appname) {
this.file = file;
this.appname = appname;
this.listener = new ArrayList<ApplicationStartedListener>();
}
public void addApplicationStartedListener(ApplicationStartedListener asl) {
this.listener.add(asl);
}
public void removeApplicationStartedListener(ApplicationStartedListener asl) {
this.listener.remove(asl);
}
public boolean isOtherInstanceRunning() {
if (!this.file.exists()) {
return false;
}
return sendMessageToRunningApplication(new ClassCheck(this.appname));
}
public boolean sendMessageToRunningApplication(final Object obj) {
this.result = false;
try {
this.client = new Socket("localhost", getPortNumber());
new Thread(new Runnable() {
public void run() {
try {
SingleInstanceController.this.oos = new ObjectOutputStream(SingleInstanceController.this.client.getOutputStream());
SingleInstanceController.this.ois = new ObjectInputStream(SingleInstanceController.this.client.getInputStream());
SingleInstanceController.this.oos.writeObject(obj);
SingleInstanceController.this.oos.flush();
SingleInstanceController.this.result = SingleInstanceController.this.ois.readBoolean();
} catch (IOException e) {
SingleInstanceController.this.result = false;
}
}
}).start();
for (int i = 0; i < 10; i++) {
if (this.result == true) {
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.client.close();
return this.result;
} catch (IOException e) {
return false;
}
}
public boolean registerApplication() {
try {
if (!this.file.exists()) {
if (!this.file.getParentFile().mkdirs() && !this.file.getParentFile().exists()) {
return false;
}
if (!this.file.createNewFile()) {
return false;
}
}
BufferedWriter wuffy = new BufferedWriter(new FileWriter(this.file));
int port = getFreeServerSocket();
if (port != -1) {
startServer();
}
wuffy.write(String.valueOf(port));
wuffy.close();
return true;
} catch (IOException e) {
return false;
}
}
protected void messageArrived(Object obj) {
for (ApplicationStartedListener asl : this.listener) {
asl.messageArrived(obj);
}
}
protected void applicationStartet() {
for (ApplicationStartedListener asl : this.listener) {
asl.applicationStarted();
}
}
protected void foreignApplicationStarted(String name) {
for (ApplicationStartedListener asl : this.listener) {
asl.foreignApplicationStarted(name);
}
}
private int getPortNumber() {
try {
BufferedReader buffy = new BufferedReader(new FileReader(this.file));
int port = Integer.parseInt(buffy.readLine().trim());
buffy.close();
return port;
} catch (Exception e) {
return -1;
}
}
private void startServer() {
new Thread(new Runnable() {
public void run() {
while (true) {
try {
SingleInstanceController.this.client = SingleInstanceController.this.server.accept();
if (SingleInstanceController.this.client.getInetAddress().isLoopbackAddress()) {
new Thread(new Runnable() {
public void run() {
try {
SingleInstanceController.this.oos = new ObjectOutputStream(SingleInstanceController.this.client.getOutputStream());
SingleInstanceController.this.ois = new ObjectInputStream(SingleInstanceController.this.client.getInputStream());
Object obj = SingleInstanceController.this.ois.readObject();
if (obj instanceof ClassCheck) {
if (obj.toString().equals(SingleInstanceController.this.appname)) {
SingleInstanceController.this.oos.writeBoolean(true);
applicationStartet();
} else {
SingleInstanceController.this.oos.writeBoolean(false);
foreignApplicationStarted(obj.toString());
}
} else {
messageArrived(obj);
SingleInstanceController.this.oos.writeBoolean(true);
}
SingleInstanceController.this.oos.flush();
SingleInstanceController.this.client.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
private int getFreeServerSocket() {
for (int i = 2000; i < 10000; i++) {
try {
this.server = new ServerSocket(i);
return i;
} catch (IOException ignore) {
}
}
return -1;
}
}

My vote goes to locking on a port (i think this is what you mean by socket). I don't know the exact reason for this. But in fact i come across only this as a solution in most practical projects. Though i will be happy to hear the alternative ways.

In response to your question, the port solution will keep more resources from the machine:
- You will keep a port locked: ports are limited and you may find problems with firewalls or other programs listening on the same port.
- You'll need an active thread.
The file solution will use less resources from the machine, to avoid locking the file forever you need to add a thread, to delete the file, in the addShutdownHook method from Runtime.

the serversocket solution is cross-platform . And will not be vulnerable to the program crashing and not resetting the lock.

File lock is better way to do- imo. When you create the file in User's Home directory, this will still work in a multi-user environment.
I came across - JUnique - haven't had a chance to use it
http://www.sauronsoftware.it/projects/junique/manual.php

I know that this question is pretty old, but I have to solve the same problem at the moment. I prefer the socket solution because I have never thought that such kind of task should have anything to do with the file system. It is better to solve the problem in memory and not in the file system I think.

Related

ScheduledThreadPoolExecutor scheduled purging memory leak

I am trying to write a enclosed class to download images from the web that will refresh every 30 seconds. I might want to download 1 image or I might want to download N images. And I might want to stop downloading a certain image at any time. I wrote the following class that works great except when I stop downloading an image memory is not being released for that task. Or if I stop all images from being downloaded memory is not released (This won't happen in production). I have tried several different ways to achieve this. My last attempt was to purge the tasks from the ScheduledThreadPoolExecutor every 30 seconds using the same executor or with the code below a separate executor. I am also providing code to test releasing memory, although my example stops all images from being downloaded when in a real life usage I should be able only stop one image and the memory be released from that one task.
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
public class ImageLoadTask implements Runnable {
private static ScheduledThreadPoolExecutor taskExecutorService = new ScheduledThreadPoolExecutor(500);
private static ScheduledThreadPoolExecutor purgeExecutorService = new ScheduledThreadPoolExecutor(500);
private static Runnable purgeRunnable = () -> purge();
private ScheduledFuture<?> scheduledFuture;
private URL pictureURL;
private Consumer<BufferedImage> successMethod;
private Consumer<String> failMethod;
private ImageURLStreamHandler streamHandler = new ImageURLStreamHandler();
private boolean displaySuccess = false;
private boolean displayError = false;
private boolean isCancelled = false;
static {
taskExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
taskExecutorService.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
taskExecutorService.setRemoveOnCancelPolicy(true);
purgeExecutorService.scheduleWithFixedDelay(purgeRunnable, 30L, 30L, TimeUnit.SECONDS);
}
public ImageLoadTask(String url) {
try {
this.pictureURL = new URL(url);
} catch (MalformedURLException e) {
if(failMethod != null) {
failMethod.accept(e.getMessage()); ;
}
if(displayError) {
System.out.println("(ImageLoadTask) URL is malformed: " + url+"\n"+ e.getMessage());
}
}
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod) {
this(url);
this.successMethod = successMethod;
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod, Consumer<String> failMethod) {
this(url, successMethod);
this.failMethod = failMethod;
}
public void start() {
scheduledFuture = taskExecutorService.scheduleAtFixedRate(this, 0L, 30L, TimeUnit.SECONDS);
}
public void stop() {
if(isCancelled)
return;
isCancelled = true;
scheduledFuture.cancel(true);
scheduledFuture = null;
pictureURL = null;
successMethod = null;
failMethod = null;
streamHandler = null;
taskExecutorService.remove(this);
taskExecutorService.purge();
}
public static void purge() {
System.out.println("Purging");
taskExecutorService.purge();
}
#Override
public void run() {
if(!isCancelled) {
try {
BufferedImage image = loadImage(pictureURL);
if(displaySuccess) {
System.out.println("Image received for url " + pictureURL);
}
if(successMethod != null && !isCancelled) {
successMethod.accept(image); ;
}
} catch (IOException e) {
if(failMethod != null && !isCancelled) {
failMethod.accept(e.getMessage());
}
if(displayError) {
System.out.println("Error occured retrieving image for url: " + pictureURL +"\n"+ e.getMessage());
}
}
}
}
public void displayError(boolean displayError) {
this.displayError = displayError;
}
public void displaySuccess(boolean displaySuccess) {
this.displaySuccess = displaySuccess;
}
private BufferedImage loadImage(URL input) throws IOException {
if (input == null) {
throw new IllegalArgumentException("input == null!");
}
InputStream istream = null;
try {
istream = streamHandler.openConnection(input).getInputStream();
} catch (IOException e) {
throw new IIOException("Can't get input stream from URL!", e);
}
ImageInputStream stream = ImageIO.createImageInputStream(istream);
BufferedImage bi;
try {
bi = ImageIO.read(stream);
if (bi == null) {
stream.close();
}
} finally {
istream.close();
}
return bi;
}
#Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
}
class ImageURLStreamHandler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL url) throws IOException {
URL target = new URL(url.toString());
URLConnection connection = target.openConnection();
// Connection settings
connection.setConnectTimeout(60000); // 1 min
connection.setReadTimeout(60000); // 1 min
return connection;
}
}
}
Test App:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageLoadTaskTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Gui gui = new Gui();
}
});
}
static class Gui extends JFrame {
private static final long serialVersionUID = 1L;
private List<ImageLoadTask> tasks = new ArrayList<>();
private boolean running = false;
private JButton startStopButton = new JButton("Start");
private JButton purgeButton = new JButton("Purge");
private ActionListener startStopListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(running) {
stopTasks();
} else {
startTasks();
}
}
};
private ActionListener purgeListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ImageLoadTask.purge();
}
};
public Gui() {
setTitle("Image Load Task Test");
setBounds(250, 250, 300, 150); // Size
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPanel = new JPanel();
setContentPane(contentPanel);
startStopButton.addActionListener(startStopListener);
contentPanel.add(startStopButton);
purgeButton.addActionListener(purgeListener);
contentPanel.add(purgeButton);
setVisible(true);
}
private void startTasks() {
running = true;
System.out.println("Starting tasks");
for(int i = 0; i < 2500; i++) {
ImageLoadTask task = new ImageLoadTask("http://placehold.it/120x120&text=image" + i, this::success, this::fail);
task.start();
tasks.add(task);
}
startStopButton.setText("Stop");
}
private void stopTasks() {
running = false;
System.out.println("Stopping " + tasks.size() + " tasks");
for(ImageLoadTask task : tasks) {
task.stop();
}
tasks.clear();
startStopButton.setText("Start");
System.out.println("Stopped tasks ");
//ImageLoadTask.purge();
}
private void success(BufferedImage image) {
//System.out.println("Success!");
}
private void fail(String message) {
//System.out.println("Fail! "+ message);
}
}
}
You don't close your 'stream' when you interrupt ImageIO.read(stream).

LibGDX Server Thread

I am currently developing a MOBA like game with libGDX, but I am struggling with the Server. Currently I have got a thread for every connection and a critical region wich stores the data. My problem is, that there are Threads, which are writing into the critical region and reading from it simultaneously. Can somebody help me out?
Here are my classes:
Clientthread:
package at.mobaserver;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import at.motm.model.GameObject;
import at.motm.model.Projectile;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Rectangle;
public class ClientConnection implements Runnable
{
private ObjectInputStream ois;
private ObjectOutputStream oos;
private Socket socket;
private GameServer server;
private GameObject receivedObject;
private Rectangle r;
private Projectile projectile;
private boolean found;
public ClientConnection(Socket socket, GameServer server)
{
try
{
this.socket = socket;
this.oos = new ObjectOutputStream(socket.getOutputStream());
this.ois = new ObjectInputStream(socket.getInputStream());
this.server = server;
this.receivedObject = null;
this.projectile = null;
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run()
{
try
{
this.oos.writeLong(Thread.currentThread().getId());
this.oos.flush();
}
catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
while (true)
{
try
{
// Add GameObjects to list
// this.server.getMutex().lock();
this.oos.reset();
// Hitdetection
if (this.projectile != null)
{
projectileHitDetection();
}
// Write Objects to client
this.oos.writeObject(this.server.getSendingList());
this.oos.flush();
// Add Objects to Severobject Arraylist
// this.server.getMutex().lock();
addObjectsToList();
// Add Projectiles to Serverobject Arraylist
readProjectiles();
// this.server.getMutex().unlock();
// this.server.getMutex().unlock();
}
catch (ClassNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
break;
}
}
}
private void readProjectiles() throws ClassNotFoundException, IOException
{
synchronized (this.server.getProjectiles())
{
this.projectile = (Projectile) this.ois.readObject();
if (this.projectile != null)
{
this.found = false;
for (int i = 0; i < this.server.getProjectiles().size(); i++)
{
if (this.server.getProjectiles().get(i).getThreadID() == Thread
.currentThread().getId())
{
this.server.getProjectiles().set(i, this.projectile);
this.found = true;
break;
}
}
if (!this.found)
{
this.server.getProjectiles().add(this.projectile);
}
}
}
}
private void addObjectsToList() throws ClassNotFoundException, IOException
{
synchronized (this.server.getObjects())
{
this.receivedObject = (GameObject) this.ois.readObject();
this.found = false;
for (int i = 0; i < this.server.getObjects().size(); i++)
{
if (this.server.getObjects().get(i).getThreadID() == Thread
.currentThread().getId())
{
this.server.getObjects().set(i, this.receivedObject);
this.found = true;
break;
}
}
if (!this.found)
{
this.server.addObject(this.receivedObject);
}
}
}
private void projectileHitDetection()
{
synchronized (this.server.getObjects())
{
this.r = new Rectangle(this.projectile.getPosition().x,
this.projectile.getPosition().y,
this.projectile.getWidth(), this.projectile.getHeight());
for (int i = 0; i < this.server.getObjects().size(); i++)
{
if (this.server.getObjects().get(0).getTeam() != this.projectile
.getTeam())
{
Rectangle r1 = new Rectangle(this.server.getObjects()
.get(0).getPosition().x, this.server.getObjects()
.get(0).getPosition().y, this.server.getObjects()
.get(0).getWidth(), this.server.getObjects().get(0)
.getHeight());
if (Intersector.overlaps(this.r, r1))
{
System.out.println("test");
this.server.getProjectiles().remove(this.projectile);
break;
}
}
}
}
}
}
Critical region:
package at.mobaserver;
import java.util.ArrayList;
import at.motm.model.GameObject;
import at.motm.model.Projectile;
public class GameServer
{
private ArrayList<GameObject> objects;
private ArrayList<Projectile> projectiles;
private ArrayList<GameObject> sendList;
public GameServer()
{
this.objects = new ArrayList<GameObject>();
this.projectiles = new ArrayList<Projectile>();
this.sendList = new ArrayList<GameObject>();
}
public ArrayList<GameObject> getSendingList()
{
synchronized (this.sendList)
{
this.sendList.clear();
for (GameObject o : this.objects)
{
if (o.isAlive())
{
this.sendList.add(o);
}
}
this.sendList.addAll(this.projectiles);
return this.sendList;
}
}
public ArrayList<GameObject> getObjects()
{
synchronized (this.objects)
{
return this.objects;
}
}
public void setObjects(ArrayList<GameObject> objects)
{
synchronized (objects)
{
this.objects = objects;
}
}
public void addObject(GameObject o)
{
synchronized (this.sendList)
{
this.objects.add(o);
}
}
public ArrayList<Projectile> getProjectiles()
{
synchronized (this.projectiles)
{
return this.projectiles;
}
}
public void setProjectiles(ArrayList<Projectile> projectiles)
{
synchronized (this.projectiles)
{
this.projectiles = projectiles;
}
}
public void addProjectile(Projectile o)
{
synchronized (this.sendList)
{
this.projectiles.add(o);
}
}
}
I have solved my problem. I used CopyAndWriteArrayList instead of normal ArrayLists.

Is the transfer data for transferables from outside the jvm null by default when the TransferHandler canImport method gets called?

I'm working on Drag & Drop support for a JList component and I would like to be able to support files dragged in from my file browser. I however only want to allow files and not folders to be dragged in. Thus I'm trying to check for it in my TransferHandler like this:
#Override
public boolean canImport(TransferSupport support) {
if (support.getComponent().equals(this.resourceFileList)) {
if (!support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
return false;
} else {
try {
// get file list
Transferable transferable = support.getTransferable();
Object transferData = transferable.getTransferData(DataFlavor.javaFileListFlavor);
// check for folders
boolean containsFiles = false;
List files = (List) transferData;
for (int i = 0; i < files.size(); i++) {
File file = (File) files.get(i);
if (!file.isDirectory()) {
containsFiles = true;
}
}
// return file indicator
return containsFiles;
} catch (IOException | UnsupportedFlavorException e) {
System.out.println("Unable to check for folders due to the following exception:\n" + e);
return false;
}
}
} else {
return false;
}
}
Unfortunately transferData seems to be null here. It is however not when the TransferHandler's importData method gets called. Does anyone have any clue if this is a bug of some sorts or desired behaviour?
I'm on a Mac (OSX 10.8) if it makes a difference and Java version is 1.7.0_21.
Thanks in advance!
EDIT:
Here's a short SSCCE for anyone to test. Just a drag a file into the list and watch the console.
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;
public class DNDIssue extends TransferHandler {
#Override
public boolean canImport(TransferSupport support) {
if (support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
try {
Transferable transferable = support.getTransferable();
Object transferData = transferable.getTransferData(DataFlavor.javaFileListFlavor); // <-- this returns null
System.out.println(transferData); // null
} catch (IOException | UnsupportedFlavorException e) {
System.out.println("Unable to check for folders due to the following exception:\n" + e);
} finally {
return false;
}
} else {
return false;
}
}
public static void main(String[] args) {
// create window
JFrame window = new JFrame("DNDIssue");
window.setSize(640, 480);
window.setLocation(100, 100);
// create list with model and set transfer handler
JList<File> list = new JList<File>(new DefaultListModel<File>());
list.setTransferHandler(new DNDIssue());
// add enclosing scroll pane and display window
window.getContentPane().add(new JScrollPane(list));
window.setVisible(true);
}
}
Your SSCCE works fine for me using JDK 1.7.0_21 on Windows 7 64bit.
Could you try following code?
Transferable transferable = support.getTransferable();
System.out.println("----");
String mt = DataFlavor.javaFileListFlavor.getMimeType();
for(DataFlavor df: transferable.getTransferDataFlavors()) {
System.out.println(df.getMimeType());
System.out.println(" "+df.getMimeType().equals(mt));
}
SSCCE 1.0: delete
SSCCE 1.1: Override TransferHandler#importData(...) version
InvalidDnDOperationException: No drop current
java.awt.dnd.InvalidDnDOperationException: No drop current
at sun.awt.dnd.SunDropTargetContextPeer.getTransferData(SunDropTargetContextPeer.java:245)
at sun.awt.datatransfer.TransferableProxy.getTransferData(TransferableProxy.java:73)
at java.awt.dnd.DropTargetContext$TransferableProxy.getTransferData(DropTargetContext.java:376)
at FileTransferHandler.canImport(DNDIssueTest.java:64)
The problem is possibly related to Bug ID 6759788 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6759788
via: http://www.coderanch.com/t/466319/GUI/java/Drag-Drop
SSCCE 1.2: Ignore InvalidDnDOperationException
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.io.*;
import java.util.List;
import javax.swing.*;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;
public class DNDIssueTest {
public JComponent makeUI() {
// create list with model and set transfer handler
JList<File> list = new JList<File>(new DefaultListModel<File>());
list.setDropMode(DropMode.INSERT);
list.setTransferHandler(new FileTransferHandler());
return new JScrollPane(list);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new DNDIssueTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class FileTransferHandler extends TransferHandler {
private static final boolean DEBUG = true;
#SuppressWarnings("unchecked")
#Override public boolean importData(TransferSupport support) {
try {
List files = (List)support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
System.out.println("importData");
if(!hasDirectory(files) && canImport(support)) {
JList list = (JList)support.getComponent();
DefaultListModel model = (DefaultListModel)list.getModel();
for(Object o: files) {
model.addElement(o);
}
return true;
}
} catch(Exception ex) {
ex.printStackTrace();
}
return false;
}
private boolean hasDirectory(List list) {
System.out.println("hasDirectory check");
for (Object o: list) {
if(o instanceof File) {
File file = (File) o;
if (file.isDirectory()) {
return true;
}
}
}
return false;
}
#Override public boolean canImport(TransferSupport support) {
if(!support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
return false;
}
if(!DEBUG) return true;
Transferable transferable = support.getTransferable();
try {
Object transferData = transferable.getTransferData(DataFlavor.javaFileListFlavor);
//System.out.println(transferData); // null
System.out.println("canImport");
return !hasDirectory((List)transferData);
} catch (Exception e) {
//e.printStackTrace();
System.out.println("*** Ignore InvalidDnDOperationException ***");
return true;
}
}
}
SSCCE 2.0: DropTargetListener version
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.event.*;
import java.io.*;
import java.util.List;
import javax.swing.*;
public class DNDIssueTest2 {
public JComponent makeUI() {
final DefaultListModel<File> model = new DefaultListModel<>();
JList<File> list = new JList<File>(model);
DropTargetListener dtl = new DropTargetAdapter() {
#Override public void dragOver(DropTargetDragEvent dtde) {
if(dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
Transferable transferable = dtde.getTransferable();
Object transferData = null;
try {
transferData = transferable.getTransferData(DataFlavor.javaFileListFlavor);
} catch(Exception ex) {
dtde.rejectDrag();
return;
}
List<File> files = (List<File>)transferData;
for (int i = 0; i < files.size(); i++) {
File file = (File) files.get(i);
if (file.isDirectory()) {
dtde.rejectDrag();
return;
}
}
dtde.acceptDrag(DnDConstants.ACTION_COPY);
return;
}
dtde.rejectDrag();
}
#Override public void drop(DropTargetDropEvent dtde) {
try {
if(dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
dtde.acceptDrop(DnDConstants.ACTION_COPY);
Transferable transferable = dtde.getTransferable();
List list = (List)transferable.getTransferData(DataFlavor.javaFileListFlavor);
for(Object o: list) {
if(o instanceof File) {
File file = (File) o;
model.addElement(file);
}
}
dtde.dropComplete(true);
return;
}
} catch(UnsupportedFlavorException ufe) {
ufe.printStackTrace();
} catch(IOException ioe) {
ioe.printStackTrace();
}
dtde.rejectDrop();
}
};
new DropTarget(list, DnDConstants.ACTION_COPY, dtl, true);
return new JScrollPane(list);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new DNDIssueTest2().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}

JProgressBar won't update

I'm trying the code I found on the voted answer from this question: Download file using java apache commons?
It's a download application, take a little look, (I'm not much familiar with JFrames and ActionEvents)
Download.java
package main;
public class Download extends JFrame implements Runnable{
public static int total;
public static int done;
private static class ProgressListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
done = (int)((DownloadCountingOutputStream) e.getSource()).getByteCount();
jbar.repaint();
DownloadCountingOutputStream.parent.draw((int)((DownloadCountingOutputStream) e.getSource()).getByteCount());//redraw
DownloadCountingOutputStream.parent.repaint();
}
}
public static JProgressBar jbar = new JProgressBar();
public void draw(int downloaded){System.out.println("downloaded: "+downloaded+ " Total: "+total);
if (downloaded== 0){
Container cont = new Container();
setDefaultCloseOperation(3);
setSize(600, 450);
setResizable(false);
setVisible(true);
cont.add(jbar);
jbar.setBounds(40, 50, 500, 50);
jbar.setMaximum(total);//The total value of bytes to download
//jbar.setValue(50);
add(cont);
jbar.setVisible(true);
}
jbar.setValue(downloaded);
//This should update the value of the progress Bar
}
public void run() {
URL dl = null;
File fl = null;
OutputStream os = null;
InputStream is = null;
ProgressListener progressListener = new ProgressListener();
draw(done);
try {
fl = new File(System.getProperty("user.home").replace("\\", "/") + "/Desktop/afile.rar");
dl = new URL("https://dl.dropbox.com/u/48076798/afile.rar");
os = new FileOutputStream(fl);
is = dl.openStream();
total = Integer.parseInt(dl.openConnection().getHeaderField("Content-Length"));
String total = dl.openConnection().getHeaderField("Content-Length");
DownloadCountingOutputStream dcount = new DownloadCountingOutputStream(os);
dcount.setListener(progressListener);
dcount.setParent(this);
IOUtils.copy(is, dcount);
} catch (Exception e) {
System.out.println(e);
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
DownloadCountingOutputStream.java
package main;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.io.output.CountingOutputStream;
public class DownloadCountingOutputStream extends CountingOutputStream {
private ActionListener listener = null;
public static Download parent;
public DownloadCountingOutputStream(OutputStream out) {
super(out);
}
public void setListener(ActionListener listener) {
this.listener = listener;
}
public void setParent(Download o){
parent = o;
}
#Override
protected void afterWrite(int n) throws IOException {
super.afterWrite(n);
if (listener != null) {
listener.actionPerformed(new ActionEvent(this, 0, null));
}
}
}
It's difficult to tell from the code sample you've provide...
The main cause of this problem is trying to update the UI while blocking from the Event Dispatching Thread (EDT).
It's important to NEVER do any long running or blocking operations within the EDT as this will prevent repaint requests from been acted upon.
For more information have a read through Concurrency in Swing
The example below demonstrates the use of a SwingWorker that provides progress updates that are re-synced with the UI
public class TestProgress {
public static void main(String[] args) {
new TestProgress();
}
public TestProgress() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
ProgressPane progressPane = new ProgressPane();
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(progressPane);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
progressPane.doWork();
}
});
}
public class ProgressPane extends JPanel {
private JProgressBar progressBar;
public ProgressPane() {
setLayout(new GridBagLayout());
progressBar = new JProgressBar();
add(progressBar);
}
public void doWork() {
Worker worker = new Worker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer) evt.getNewValue());
}
}
});
worker.execute();
}
}
public class Worker extends SwingWorker<Object, Object> {
#Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
int progress = Math.round(((float) index / 1000f) * 100f);
setProgress(progress);
Thread.sleep(10);
}
return null;
}
}
}

Make threads usage efficient in Java

I have coded a simple application in Java that downloads particular images from a list of html links provided. Everything was working fine until I added the feature of having to download from a list of html links rather than just one. I had to implement the wait() and notify() methods which forced me to change the approach a little. Now, the downloads work fine, but the GUI does not update while the download is in progress.
I make the 1st thread wait from HTML.java and notify it at the end of DownloadImages.java. For this I had to invoke buttonPressed class as an object rather than a thread, which is why I think my GUI won't update.
Is there a way to simplify or make thread-usage more efficient in my code? Thanks in advance.
Here is skeleton of my code:
/*Test.java*/
package my;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Test extends javax.swing.JFrame {
public static buttonPressed bp;
public static boolean alldone;
/** Creates new form Test */
public Test() {
initComponents();
}
public static class buttonPressed implements Runnable {
Thread t1, t2;
buttonPressed() {
t1 = new Thread(this, "downloadAction");
t1.start();
}
public void suspendThread() {
System.out.println("suspended");
alldone = false;
}
public synchronized void resumeThread() {
System.out.println("resumed");
alldone = true;
notify();
}
public void run() {
String[] len = new String[]{/*list of urls*/};
for (int i = 0; i &lt len.length; i++) {
System.out.println("going times: " + i);
t2 = new Thread(new HTML(), "HTMLthread");
t2.start();
synchronized (this) {
while (!alldone) {
try {
wait();
} catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
}
private void downloadActionPerformed(java.awt.event.ActionEvent evt) {
bp = new buttonPressed();
try {
bp.t1.join();
} catch (InterruptedException e) {
System.out.println("Main Thread: interrupted");
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Test().setVisible(true);
}
});
}
private javax.swing.JButton download;
public static javax.swing.JProgressBar progress;
}
/*HTML.java*/
package my;
import java.util.ArrayList;
class HTML implements Runnable {
private Thread t3;
public HTML() {
Test.bp.suspendThread();
}
public void run() {
downloadHTML();
ArrayList xyz = parseHTML();
t3 = new Thread(new DownloadImages(xyz), "DownDecrypt");
t3.start();
}
private void downloadHTML() {
// Downloads the HTML file
}
private ArrayList parseHTML() {
// Parses the HTML file and gets links to images
return new ArrayList();
}
}
/*DownloadImages.java*/
package my;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
class DownloadImages implements Runnable {
static int current = 0, previous = 0;
static boolean speedFlag;
ArrayList<String> links = new ArrayList<String>();
private Thread t4;
public DownloadImages(ArrayList param1) {
this.links = param1;
speedFlag = true;
}
public void run() {
t4 = new Thread(new getSpeed(), "getSpeed");
t4.start();
download(links);
}
private void download(ArrayList<String> param1) {
String[] imgurl = new String[param1.size()];
URLConnection conn = null;
InputStream is = null;
ByteArrayOutputStream bais = null;
int prog;
for (int i = 0; i < param1.size(); i++) {
current = 0;
imgurl[i] = param1.get(i);
try {
conn = new URL(imgurl[i]).openConnection();
int fsize = conn.getContentLength();
is = new BufferedInputStream(conn.getInputStream());
bais = new ByteArrayOutputStream();
byte[] byteChunk = new byte[1024];
int n;
while ((n = is.read(byteChunk)) > 0) {
bais.write(byteChunk, 0, n);
current = current + 1024;
prog = (int) (current * 100.0 / fsize);
Test.progress.setValue(prog);
}
} catch (MalformedURLException ex) {
Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
byte[] imgBytes = bais.toByteArray();
try {
FileOutputStream fos = new FileOutputStream(i + ".jpg");
fos.write(imgBytes);
fos.flush();
fos.close();
} catch (FileNotFoundException ex) {
System.out.println("FileNotFoundException : " + ex);
} catch (IOException e) {
e.printStackTrace();
}
}
speedFlag = false;
// Resume the thread to start downloading the next link
Test.bp.resumeThread();
}
private static class getSpeed implements Runnable {
int kbytesPerSecond;
private final int fireTime;
public getSpeed() {
fireTime = 1000;
}
public void run() {
while (speedFlag) {
try {
Thread.sleep(fireTime);
} catch (InterruptedException ex) {
Logger.getLogger(getSpeed.class.getName()).log(Level.SEVERE, null, ex);
}
kbytesPerSecond = (((current - previous) / 1024) / (fireTime / 1000));
System.out.println(kbytesPerSecond);
previous = current;
}
}
}
}
As far as the GUI is concerned you need to read about Swing concurrency. In short, use SwingWorker.
Mind that you use old AWT stuff (java.awt.EventQueue).
I suggest you have an ExecutorService like Executors.newCachedThreadPool and submit() the tasks to it. Collect the Future objects so you know when they are done. This will be more efficient and manageable than creating Threads all over the place.
You can have just one pool like
static final ExecutorService POOL = Executors.newCachedThreadPool();
to submit a task
POOL.submit(new Callable<Void>() {
public Void call() throws InterruptedException {
while (speedFlag) {
Thread.sleep(1000);
kbytesPerSecond = (current - previous) / 1024;
System.out.println(kbytesPerSecond);
previous = current;
}
}
});
Even better for repeating tasks is to use a scheduled executor service.
static final ScheduledExecutorService POOL = Executors.newScheduledThreadPool(4);
Future task = POOL.scheduleAtFixedRate(new Runnable() {
public void run() {
kbytesPerSecond = (current - previous) / 1024;
System.out.println(kbytesPerSecond);
previous = current;
}
}, 1, 1, TimeUnit.SECONDS);
// to end the task
task.cancel(false);

Categories