how can I show a JProgressBar component like on the loading of a bin file?
I can only found solutions for iterative bin read and I'm using an object reading like:
CustomObj test = (CustomObj) in.readObject();
Cheers
If you can't measure the progress of the process, then you can only specify the "indeterminate mode" of the progress bar. When in this mode, the progress bar will indicate that it is working, but the completion of the process is unknown.
JProgressBar progress = new JProgressBar();
progress.setIndeterminate(true);
I recommend to do two things:
Creating a wrapping class around your original inputstream so that you can monitor the bytes that are read from it. Basically, you extends InputStream and delegate everything to the original stream (except a few methods) and in the read() method, you make sure that you notify some listener.
I guess that if you want a progress bar, it means that the loading operation takes a while and you want to provide feedback to the user. Long running task cannot run directly on the EDT (so typically, you cannot perform your task in an actionPerformed method). You therefore need to delegate the work to another Thread, by using a SwingWorker for example. If you don't this, then the UI will freeze and the feedback will not be viewable by the user.
This being, said it may seem complex or not trivial. Therefore, here some short example, that illustrates all this and works:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class TestProgressBar {
// Some simple listener interface to get a callback as bytes are being read
public static interface ProgressListener {
public void notifyByteRead();
}
// The wrapping input stream that will call the listener as bytes are being read
public static class ProgressInputStream extends InputStream {
private InputStream in;
#Override
public int read() throws IOException {
int read = in.read();
if (read > -1) {
// Here we notify the listener
listener.notifyByteRead();
}
return read;
}
#Override
public long skip(long n) throws IOException {
return in.skip(n);
}
#Override
public int available() throws IOException {
return in.available();
}
#Override
public void close() throws IOException {
in.close();
}
#Override
public void mark(int readlimit) {
in.mark(readlimit);
}
#Override
public void reset() throws IOException {
in.reset();
}
#Override
public boolean markSupported() {
return in.markSupported();
}
private ProgressListener listener;
public ProgressInputStream(InputStream in, ProgressListener listener) {
this.in = in;
this.listener = listener;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
init();
}
});
}
public static void init() {
// 1. Let's create a big object with lots of data
List<Long> object = new ArrayList<Long>();
Random random = new Random();
for (int i = 0; i < 1e6; i++) {
object.add(random.nextLong());
}
// 2. We write it to a temp file
File tempFile = null;
ObjectOutputStream oos = null;
try {
tempFile = File.createTempFile("Test", ".bin");
tempFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempFile);
oos = new ObjectOutputStream(new BufferedOutputStream(fos));
oos.writeObject(object);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (tempFile == null) {
System.exit(1);
}
// 3. Now let's build a UI to load that
final File theFile = tempFile;
JFrame frame = new JFrame("Test ghost text");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout());
final JProgressBar bar = new JProgressBar(0, (int) tempFile.length());
JButton button = new JButton("load");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
bar.setValue(0);
// Declare and implement a Swing worker that will run in another thread
SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>() {
#Override
protected void process(List<Integer> chunks) {
// Here we are on the EDT, so we can safely notify the progressbar
super.process(chunks);
bar.setValue(bar.getValue() + chunks.size());
}
#Override
protected Void doInBackground() throws Exception {
// Here we are not in the EDT, we perform the task but don't modify anything in the UI
ProgressInputStream pis = new ProgressInputStream(new BufferedInputStream(new FileInputStream(theFile)),
new ProgressListener() {
#Override
public void notifyByteRead() {
publish(1); // the value that is sent here could be anything, we don't use it.
}
});
ObjectInputStream ois = new ObjectInputStream(pis);
try {
List<Long> readObject = (List<Long>) ois.readObject();
System.err.println("Loaded " + readObject.size() + " long values");
} catch (Exception e) {
e.printStackTrace();
} finally {
pis.close();
}
return null;
}
};
// Start the worker
worker.execute();
}
});
panel.add(bar);
panel.add(button, BorderLayout.EAST);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
Subclass java.io.FilteredInputStream to count the number of bytes being read and insert it between your ObjectInputStream and the underlying InputStream being read.
You can update the progress bar by sampling the running count or using a callback built in to your subclass.
Example:
public class CountingInputStream extends FilteredInputStream {
private int numBytes;
public CountingInputStream(InputStream inputStream){
this(inputStream);
}
public int getNumBytes(){
return numBytes;
}
#Override
public int read() {
int b = super.read();
if(b != -1){
countBytes(1);
}
return b;
}
#Override
public int read(byte[] b){
int n = super.read(b);
if(n >= 0){
countBytes(n);
}
return n;
}
#Override
public int read(byte[] b, int off, int len){
int n = super.read(b, off, len);
if(n >= 0){
countBytes(n);
}
return n;
}
private void countBytes(int n){
numBytes += n;
}
}
It could be used like below (assume InputStream is your source of data).:
InputStream is = ...;
CountingInputStream cis = new CountingInputStream(is)
ObjectInputStream ois = new ObjectInputStream(cis);
ois.readObject();
You can sample cis.getNumBytes() from a different thread (potentially with a Swing timer) and use the returned value to update a JProgressBar
Related
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).
I know I must be missing something very obvious, but whenever I try to use the ProgressMonitorInputStream when copying a file, I never get the ProgressDialog popup.
The examples I see don't seem to do much other than wrap their input stream within the ProgressMonitorInputStream.
The docs say:
This creates a progress monitor to monitor the progress of reading the input stream. If it's taking a while, a ProgressDialog will be popped up to inform the user. If the user hits the Cancel button an InterruptedIOException will be thrown on the next read. All the right cleanup is done when the stream is closed.
Here is an extremely simple example I put together that never pops up the dialog with a large file even if I setMillisToPopup() to an insanely small number.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.ProgressMonitorInputStream;
import javax.swing.SwingWorker;
public class ProgressBarDemo extends JFrame {
private static final long serialVersionUID = 1L;
private JButton button;
ProgressBarDemo()
{
button = new JButton("Click me!");
ButtonActionListener bal = new ButtonActionListener();
button.addActionListener(bal);
this.getContentPane().add(button);
}
private class ButtonActionListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
Worker worker = new Worker();
worker.execute();
button.setEnabled(false);
}
}
public void go() {
this.setLocationRelativeTo(null);
this.setVisible(true);
this.pack();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private class Worker extends SwingWorker<Void, Void>
{
private void copyFile() {
File file = new File("/Users/mypath/Desktop/WirelessDiagnostics.tar.gz");
BufferedInputStream bis;
BufferedOutputStream baos;
try {
bis = new BufferedInputStream(new FileInputStream(file));
ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(
ProgressBarDemo.this,
"Reading... " + file.getAbsolutePath(),
bis);
pmis.getProgressMonitor().setMillisToPopup(10);
baos = new BufferedOutputStream(new FileOutputStream("/Users/mypath/Desktop/NewWirelessDiagnostics.tar.gz"));
byte[] buffer = new byte[2048];
int nRead = 0;
while((nRead = pmis.read(buffer)) != -1) {
baos.write(buffer, 0, nRead);
}
pmis.close();
baos.flush();
baos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
protected Void doInBackground() throws Exception {
// TODO Auto-generated method stub
copyFile();
return null;
}
#Override
protected void done() {
button.setEnabled(true);
}
}
}
public class TestProj {
public static void main(String[] args) {
ProgressBarDemo demo = new ProgressBarDemo();
demo.go();
}
}
Any suggestions?
You are calling copyFile from within the context of the Event Dispatching Thread, this means the EDT is unable to respond to new events or paint requests until after the method returns.
Try placing the call within it's own Thread context...
Thread t = new Thread(new Runnable(
public void run() {
copyFile();
}
));
t.start();
Equally, you could use a SwingWorker, it's a little bit of overkill, but you get the benefit of the PropertyChangeListener or it's done method, which could be used to re-enable the JButton, should you want to prevent people from clicking the button while a copy operation is in progress
See Concurrency in Swing and Worker Threads and SwingWorker for more details
Updated with example
Copying a 371mb file, across the local disk...
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.ProgressMonitorInputStream;
import javax.swing.SwingWorker;
public class ProgressBarDemo extends JFrame {
private static final long serialVersionUID = 1L;
private JButton button;
ProgressBarDemo() {
button = new JButton("Click me!");
ButtonActionListener bal = new ButtonActionListener();
button.addActionListener(bal);
this.getContentPane().add(button);
}
public void go() {
this.setLocationRelativeTo(null);
this.setVisible(true);
this.pack();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void copyFile() {
File file = new File("...");
BufferedInputStream bis;
BufferedOutputStream baos;
try {
bis = new BufferedInputStream(new FileInputStream(file));
ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(
this,
"Reading... " + file.getAbsolutePath(),
bis);
pmis.getProgressMonitor().setMillisToPopup(10);
baos = new BufferedOutputStream(new FileOutputStream("..."));
byte[] buffer = new byte[2048];
int nRead = 0;
while ((nRead = pmis.read(buffer)) != -1) {
baos.write(buffer, 0, nRead);
}
pmis.close();
baos.flush();
baos.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private class ButtonActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
SwingWorker worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
copyFile();
return null;
}
#Override
protected void done() {
button.setEnabled(true);
}
};
worker.execute();
}
}
public static void main(String[] args) {
ProgressBarDemo demo = new ProgressBarDemo();
demo.go();
}
}
Remember, there is overhead involved in setting up the window and displaying, which needs to be factored in. The system may "want" to display a window, but by the time the system has set it up and is prepared to display it, the steam may have finished copying...
Extended Example
nb: I don't really like the ProgressMonitor API as I've not been able to find where the UI is synchronised with the EDT, this can cause issues in Java 7 & 8
You could formalise the idea into a self contained worker, for example...
public class CopyWorker extends SwingWorker {
private File source;
private File dest;
private Component parent;
private ProgressMonitorInputStream pmis;
public CopyWorker(Component parent, File source, File dest) {
this.parent = parent;
this.source = source;
this.dest = dest;
}
#Override
protected Object doInBackground() throws Exception {
try (InputStream is = new FileInputStream(source)) {
try (OutputStream os = new FileOutputStream(dest)) {
pmis = new ProgressMonitorInputStream(
parent,
"Copying...",
is);
pmis.getProgressMonitor().setMillisToPopup(10);
byte[] buffer = new byte[2048];
int nRead = 0;
while ((nRead = pmis.read(buffer)) != -1) {
os.write(buffer, 0, nRead);
}
}
}
return null;
}
#Override
protected void done() {
try {
pmis.close();
} catch (Exception e) {
}
}
}
This attempts to contain the functionality, but also deals with the cleanup of the ProgressMonitorInputStream within the done method, making sure that it's done within the EDT. I'd personally attach a PropertyChangeListener to it and monitor the done property to determine when the worker has completed and examine the return result in order to pick up any exceptions, this gives you the ability to handle the exceptions in your own way and de-couples the worker from your process
Your program works on files, but when it comes to server and client streams, it
fails.
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.ProgressMonitorInputStream;
import javax.swing.SwingWorker;
public class FileReceive extends JFrame {
private static final long serialVersionUID = 1L;
private BufferedInputStream bufferInput;
private FileOutputStream fout;
private BufferedOutputStream bufferOutput;
private Socket client;
private JButton button;
private File fileinfo;
ProgressMonitorInputStream pois;
FileReceive() {
setLocationRelativeTo(null);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
receiveFile();
}
public static void main(String arg[]) {
new FileReceive();
}
public void receiveFile() {
try {
fileinfo=new File(filepath);
client=new Socket("localhost",9090);
fout=new FileOutputStream(fileinfo);
bufferOutput=new BufferedOutputStream(fout);
bufferInput=new BufferedInputStream(client.getInputStream());
pois=new ProgressMonitorInputStream(this, "reading", bufferInput);
int ch;
byte[] b=new byte[2048];
System.out.println("Receiving File");
while(-1!=(ch=pois.read(b))) {
bufferOutput.write(b,0,ch);
}
pois.close();
bufferInput.close();
bufferOutput.close();
fout.close();
client.close();
} catch (IOException e) {
JOptionPane.showMessageDialog(null, e.getMessage());
}
}
}
Im trying to make a program that downloads a file. While downloading the file there needs to have a basic counter which counts from start to end. These two functions need to be in their own threads. I have created a method/thread to download the file. I'm having trouble figuring out how to make a basic counter. Nothing fancy but it needs to count up and only display the current number. Any help would be appreciated.
import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static myutilites.methods.*;
public class Downloader
{
public static void main(String[] args) throws IOException
{
String filename = "Nexus5MahdiRom.zip";
URL link = new URL(
"http://files.mahdi-rom.com/OnePlus%20One/"
+ "OnePlus%20One%20Builds/mahdi-2.7-bacon-20140903.zip");
Thread t1 = new Thread(new Runnable()
{
#Override
public void run()
{
try
{
downloadFile(filename,link);
} catch (IOException e)
{
e.printStackTrace();
}
}
});
Thread t2 = new Thread(new Runnable()
{
#Override
public void run()
{
try {
counter(link, t1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t1.start();
println("Downloading File...");
}
public void getScreenSize()
{
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
println("width = " + d.width + " height = " + d.height);
}
public static void downloadFile(String fileName, URL link) throws IOException
{
InputStream in = new BufferedInputStream(link.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte [] buf = new byte[1024];
int n = 0;
int count = 0;
while (-1 !=(n=in.read(buf)))
{
out.write(buf, 0, n);
}
println(count);
out.close();
in.close();
byte[] response = out.toByteArray();
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(response);
fos.close();
println("Download Finished");
}
public static void locate(int row, int col)
{
System.out.print("\033["+row+";"+col+"H");
}
public static int counter(URL link, Thread t1) throws IOException
{
int count = 0;
link.openConnection();
while(t1.isAlive())
{
count++;
return count;
}
return count;
}
}
Use getAndIncrement or incrementAndGet on AtomicInteger, which is a thread-safe counter (with the counter initialized to 0, if two threads simultaneously call incrementAndGet then you're guaranteed that the counter will return 1 to one thread and 2 to the other thread, and will have a final state of 2)
First, everyone needs to know i'm relatively new to Java coding. To be more precise i'm completely new to Object Oriented Programming.
To the question.
I am trying to create a download class that updates a progress bar it was given to show its progress. And possibly anything else I decide to give it in the future to update.
The issue currently is that, in my head, this shouldn't work. I can do anything i want on the "main" method and the GUI is still responsive and quick. In my experience in past programming, this is not possible unless i thread the GUI. Why is this?
Since it works, is this ok to do it this way?
Class Main
package atomicElectronics;
import java.io.IOException;
import atomicElectronics.physical.AtomFrame;
import atomicElectronics.utility.Download;
public class Initial {
static AtomFrame atomLauncher;
public static void main(String[] args) {
atomLauncher = new AtomFrame();
atomLauncher.start();
System.out.println(Integer.MAX_VALUE);
Download theDownload = new Download();
theDownload.fileProgressBar(atomLauncher.progressBar);
try {
theDownload.exicute("http://download.videolan.org/pub/videolan/vlc/last/win64/vlc-2.1.3-win64.exe", "C:\\Users\\TrinaryAtom\\AppData\\Roaming");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// TODO Add Download Methods
// theDownload.updateBarTotal(JProgressBar);
// theDownload.updateLabelSpeed(String);
// theDownload.updateLabelTotal(String);
// theDownload.addFile(File);
// theDownload.addFiles(Files);
}
}
Class AtomFrame
package atomicElectronics.physical;
import javax.swing.JFrame;
import java.awt.FlowLayout;
import javax.swing.JProgressBar;
public class AtomFrame extends JFrame{
public JProgressBar progressBar;
private static final long serialVersionUID = 4010489530693307355L;
public static void main(String[] args){
AtomFrame testFrame = new AtomFrame();
testFrame.start();
}
public AtomFrame(){
initializeComponents();
}
public void initializeComponents(){
this.setSize(400, 400);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Atom Launcher");
this.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
progressBar = new JProgressBar();
this.add(progressBar);
//this.pack();
}
public void start() {
this.setVisible(true);
}
public void close() {
this.dispose();
}
}
Class Download
package atomicElectronics.utility;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.swing.JProgressBar;
public class Download {
private static final int BUFFER_SIZE = 4096;
private JProgressBar fileProgressBar;
public Download() {
}
public void fileProgressBar(JProgressBar fileBar) {
fileProgressBar = fileBar;
}
public void exicute(String fileURL, String saveDir) throws IOException {
URL url = new URL(fileURL);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
int responseCode = httpConn.getResponseCode();
// always check HTTP response code first
if (responseCode == HttpURLConnection.HTTP_OK) {
String fileName = "";
String disposition = httpConn.getHeaderField("Content-Disposition");
String contentType = httpConn.getContentType();
double contentLength = httpConn.getContentLength();
if (disposition != null) {
// extracts file name from header field
int index = disposition.indexOf("filename=");
if (index > 0) {
fileName = disposition.substring(index + 9,
disposition.length());
}
} else {
// extracts file name from URL
fileName = fileURL.substring(fileURL.lastIndexOf("/") + 1,
fileURL.length());
}
System.out.println("Content-Type = " + contentType);
System.out.println("Content-Disposition = " + disposition);
System.out.println("Content-Length = " + contentLength);
System.out.println("fileName = " + fileName);
// opens input stream from the HTTP connection
InputStream inputStream = httpConn.getInputStream();
String saveFilePath = saveDir + File.separator + fileName;
// opens an output stream to save into file
FileOutputStream outputStream = new FileOutputStream(saveFilePath);
double totalRead = 0;
int bytesRead = -1;
byte[] buffer = new byte[BUFFER_SIZE];
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalRead += bytesRead;
System.out.println((totalRead / contentLength) * 100);
fileProgressBar.setValue((int)((totalRead / contentLength) * 100));
}
outputStream.close();
inputStream.close();
System.out.println("File downloaded");
} else {
System.out.println("No file to download. Server replied HTTP code: " + responseCode);
}
httpConn.disconnect();
}
}
Suggestions:
Use a SwingWorker to do your background thread work.
Inside your SwingWorker, set its progress "bound" property via setProgress(int progress). The value should be between 1 and 100.
Don't have your SwingWorker/file downloader hold the JProgressBar or any Swing components.
Add a PropertyChangeListener to your SwingWorker and monitor changes in the progress property.
Never make your Swing fields (or most and and all fields) public. Limit access, and instead change object state via methods.
Read the tutorial Concurrency in Swing for the necessary details.
For example, the code below is a gross simplification and downloads no files, but should give you the idea:
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.*;
public class Initial {
static AtomFrame atomLauncher;
public static void main(String[] args) {
atomLauncher = new AtomFrame();
atomLauncher.start();
System.out.println(Integer.MAX_VALUE);
final Download theDownload = new Download();
theDownload.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if ("progress".equals(pcEvt.getPropertyName())) {
int progress = theDownload.getProgress();
atomLauncher.setProgress(progress);
}
}
});
theDownload.execute();
}
}
class AtomFrame extends JFrame {
// ********* should be private!
private JProgressBar progressBar;
private static final long serialVersionUID = 4010489530693307355L;
public static void main(String[] args) {
AtomFrame testFrame = new AtomFrame();
testFrame.start();
}
public void setProgress(int progress) {
progressBar.setValue(progress);
}
public AtomFrame() {
initializeComponents();
}
public void initializeComponents() {
this.setSize(400, 400);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Atom Launcher");
this.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
progressBar = new JProgressBar();
this.add(progressBar);
// this.pack();
}
public void start() {
this.setVisible(true);
}
public void close() {
this.dispose();
}
}
class Download extends SwingWorker<Void, Void> {
private static final long SLEEP_TIME = 300;
private Random random = new Random();
#Override
protected Void doInBackground() throws Exception {
int myProgress = 0;
while (myProgress < 100) {
myProgress += random.nextInt(10);
setProgress(myProgress);
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {}
}
return null;
}
}
I'm making a program that is saving an array of JButtons to a file .btn. here is the code that is being saved:
package avtech.software.compunav;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import javax.swing.JButton;
public class Buttons implements Serializable {
public static Button[] buttons = new Button[15];
public Buttons() {
for (int i = 0; i < buttons.length; i++) {
buttons[i] = new Button();
buttons[i].setText("Unassigned");
}
}
public JButton[] getButtons() {
return buttons;
}
public JButton getButton(int index) {
return buttons[index];
}
public void setButtonText(String txt, int index) {
buttons[index].setText(txt);
}
public void setButtonAction(String action, int index) {
}
public void save() {
try {
File dir = new File(Core.baseDir + "/bin/buttons.btn");
FileOutputStream fos = new FileOutputStream(dir);
ObjectOutputStream oos = new ObjectOutputStream(fos);
if (dir.exists())
dir.delete();
oos.writeObject(this);
oos.flush();
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Button is a class that extends JButton, and here is that code:
package avtech.software.compunav;
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.swing.JButton;
import CompuNav.main.Dialogs;
public class Button extends JButton implements ActionListener {
private String action = "";
public Button() {
addActionListener(this);
}
public void setAction(String s) {
action = s;
}
#Override
public void actionPerformed(ActionEvent arg0) {
if (action.equals(""))
return;
File file = new File(action);
Desktop dt = Desktop.getDesktop();
try {
dt.open(file);
} catch (IOException e1) {
Dialogs.msg("Could not open " + action);
}
}
}
Basically, the code is saving. there is a file called buttons.btn in the correct directory. The problem is, when I use the load method here:
try {
FileInputStream fis = new FileInputStream(baseDir
+ "/bin/buttons.btn");
ObjectInputStream ois = new ObjectInputStream(fis);
buttonsClass = (Buttons) ois.readObject();
ois.close();
} catch (Exception e) {}
after making a new object of the Buttons and saving it, I get a nullPointerException when trying to call buttonsClass.getButton(0);, implying that the JButtons are not saved when i save the class.
Any reason as to why, and any idea how to fix this?
public static Button[] buttons = new Button[15];
This variable should not be static if you want it to be serialized.