This is my first post, so if something wrong, I'll try to fix it.
Test task I am dealing with is to make file searcher with ability to navigate text, etc. I finished this part except one thing: application needs to allow user work with it while it is searching files in directories.
I've read a lot about multithreading, but still can't get answer how to do this... This is a code which executes when user presses "Choose folder" in my JFrame("GUI" class):
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
DefaultTreeModel model = (DefaultTreeModel) jTree1.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
int j = jTree1.getRowCount() - 1;
final JFileChooser fc = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter(jComboBox1.getSelectedItem().toString() + " files", jComboBox1.getSelectedItem().toString());
fc.setFileFilter(filter);
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fc.setDialogTitle("Choose a folder to fing ." + jComboBox1.getSelectedItem().toString() + " files");
jLabel4.setText("Searching files in folder, please wait");
if (fc.showOpenDialog(jButton3) == JFileChooser.APPROVE_OPTION) {
root.setUserObject("Your root");
jTextArea1.setText("");
jTextField1.setText("");
if (jTree1.getRowCount() > 0) {
for (int i = 0; i < j; i++) {
model.removeNodeFromParent((MutableTreeNode) model.getChild(root, 0));
}
}
long startTime = System.currentTimeMillis();
File f2 = new File(fc.getSelectedFile().getAbsolutePath());
String name2 = "." + jComboBox1.getSelectedItem().toString();
//FilenameFilter filter1 = (File dir, String name1) -> name1.endsWith(name2);
ArrayList<File> files = new ArrayList<>();
new NewТhread();
listf(f2, files, name2);
root.setUserObject(f2);
model.nodeChanged(root);
for (int i = 0; i < files.size(); i++) {
DefaultMutableTreeNode child = new DefaultMutableTreeNode(files.get(i).getAbsolutePath().replace(f2.toString(), ""));
root.add(child);
model.reload();
jLabel4.setText("Done! Now type text to search and press \"Search files\"");
}
System.out.println("Time used for getting files: " + (System.currentTimeMillis() - startTime) / 1000 + "sec");
JOptionPane.showMessageDialog(null, "Directory opened:" + fc.getSelectedFile().getAbsolutePath());
System.out.println("Main thread finished");
} else if (fc.showOpenDialog(jButton3) == JFileChooser.CANCEL_OPTION) {
jLabel4.setText("Please, choose folder and extension to find files");
}
}
Listf is a method to check all subdirectories too:
public ArrayList<File> listf(File directory, ArrayList<File> files, String extension) {
File[] flist = directory.listFiles();
for (int i = 0; i < flist.length; i++) {
if (flist[i].isFile() && flist[i].getName().endsWith(extension) == true) {
files.add(flist[i]);
} else if (flist[i].isDirectory()) {
listf(flist[i], files, extension);
}
}
return files;
}
NewThread is a class that I still can't get in. For now it looks like this:
class NewТhread extends Thread {
NewТhread() {
super("Demo");
start();
}
public synchronized void run() {
try {
for (int i = 3; i > 0; i--) {
Thread.sleep(500);
System.out.println(i);
GUI gui = new GUI();
gui.setVisible(true);
}
} catch (InterruptedException е) {
System.out.println("Interrupted");
}
}
After 3-2-1 count it should open new GUI object (JFrame) that should allow to make all the actions. Now all the elements of object GUI gui are frozen while main thread executes (and become unfrozen only when search finishes).
Could you help, please?
You can simply start a thread by putting only the code, which does not require user's interaction, inside the thread's run method as shown below:
new Thread()
{
public void run()
{
// code which does not require user's interaction
}
}.start();
In your case the code to be placed in run method, will be the code written in "jButton1ActionPerformed" action listener. :-)
Thanks for everyone. I've found decision with ExecutorService, it allows to not freeze project while data is loading.
Related
here is a java program that allows to display the files of each directory
the problem how to display the result in a textarea
private static void
findFilesRecursively(File file, Collection<File> all, final String extension) {
final File[] children = file.listFiles(new FileFilter() {
public boolean accept(File f) {
return f.getName().endsWith(extension) ;
}}
);
if (children != null) {
//Pour chaque fichier recupere, on appelle a nouveau la methode
for (File child : children) {
all.add(child);
findFilesRecursively(child, all, extension);
}
}
}
public static void main(String[] args)
{
//try {
final Collection<File> all = new ArrayList<File>();
// JFileChooser fc = new JFileChooser(".");
// int returnVal = fc.showOpenDialog(null);
findFilesRecursively(new File("c:\\repertoire"), all,"");
//File outputFile = new File("C:\\Users\\21365\\Desktop\\tt.txt");
//FileOutputStream fos = new FileOutputStream(outputFile);
for (int i = 0; i < all.size(); i++) {
for (File file : all) {
if(file.isDirectory()==true){
System.out.println("le repertoire \t"+file.getName()+"\t contien");
}else{
You should not iterate through your list twice - get rid of one of these 2 for loops :
for (int i = 0; i < all.size(); i++) {
for (File file : all) {
Also instead of using System.out.println(…) to print to console, just create a JFrame / JTextArea and use its append(String text) method, eg :
if (file.isDirectory() == true) {
yourTextArea.append("le repertoire \t" + file.getName() + "\t contien");
} else {
yourTextArea.append(file.getName());
}
I have generating data of users with auto-increment ID, then write it to file following these rules:
Name the file in following structure (FileCounter)_(StartID)_(EndID)
Maximum 1000 records per file
If don't have enough 1000 records to write, wait maximum 10s, if any added, write it all to file otherwise, write the remain list to file (not enough 1000), if nothing to write after wait, create empty file with naming (FileCounter)_0_0
My approach is using 2 thread, 1 thread to generate data then push it to the queue, 1 thread to take from the queue add to a list then write the list to the file.
//Generate function
public void generatedata() {
int capacity = 1678;
synchronized(users) {
for(int index = 0; index <capacity; index++) {
users.add(generateUser());
// notify to read thread
users.notifyAll();
}
}
//Write function
public void writeToFile(ArrayList<User> u) {
String fileName ="";
if(!u.isEmpty()) {
String filename = "" + (++FileCounter) + "_"+ u.get(0).getId() + "_" +
u.get(u.size() - 1).getId() + ".txt";
try {
FileWriter writer = new FileWriter(filename, true);
for (User x : u) {
System.out.println(x.toString());
writer.write(x.getId() + " | " + x.getFormatedDate() + " | " +
x.getSex() + " | " + x.getPhoneNum().getPhoneNumber() + " | " +
x.getPhoneNum().getProvider() + "\r\n");
}
writer.close();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
try {
fileName = ""+(++FileCounter) +"_0_0.txt";
File f = new File(fileName);
f.createNewFile();
} catch (IOException ex) {
Logger.getLogger(UsersManager.class.getName()).log(Level.SEVERE,
null, ex);
}
}
}
//Read function
public ArrayList<User> ReadFromQueue(ArrayList<User> u) {
while(true) {
try {
int size = users.size();
if(users.isEmpty() && u.size() < 1000) {
users.wait(10000);
if(isChanged(size)) {
System.out.println("Size changed here");
u.add(users.take());
}
else return u;
}
if(u.size() == 1000) {
System.out.println("Check the size is 1000");
return u;
}
u.add(users.take());
} catch (InterruptedException ex) {
Logger.getLogger(UsersManager.class.getName()).log(Level.SEVERE,
null, ex);
}
}
It work fine when I run 1 thread to generate data, 1 thread to read then write data to file but when I use 2++ thread for each generate thread of write thread, There are 1 problems :
The list written in the file still has 1000 records as expected but not sequential at all, it only ascending order.
My output is like:
1_2_1999.txt
2_1_2000.txt
3_2001_3000.txt
My expected output is like:
1_1_1000.txt
2_1001_2000.txt
....
Thanks in advance!
using the thread approach is best for when you do not want to control the amount per file. but since you have a constraint of 1000 records, it's probably easier to use a counter;
public class DataReaderWriter(){
//keeps track of where you left off at, which row in source data.
static int currentRowInSourceData = 0;
public static void main(String[] args){
List<ContactRecord> contacts = getMoreData();
writeRecords(contacts);
}
writeRecords(List<ContactRecord> contacts){
int maxRecords = currentRowInSourceData+1000;
for(int i = currentRowInSourceData;i<maxRecords;i++){
ContactRecord c = contacts.get(i);
writeToFile(c);
currentRowInSourceData++;
}
}
I had a project where I needed to create 90 second previews from larger MP4 files. What I did was to have multiple threads start up with access to a shared Queue of file names. Each thread consumes work from the Queue by using queue.poll().
Here is the Constructor:
public Worker(Queue<String> queue, String conferenceYear, CountDownLatch startSignal, CountDownLatch doneSignal) {
this.queue = queue;
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
Then, as I said above, I keep polling for data:
public void run() {
while (!queue.isEmpty()) {
String fileName = queue.poll() + ".mp4";
File f = new File("/home/ubuntu/preview_" + fileName);
if (fileName != null && !f.exists()) {
System.out.println("Processing File " + fileName + "....");
I started these threads in another class called WorkLoad:
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
BlockingQueue<String> filesToDownload = new LinkedBlockingDeque<String>(1024);
BlockingQueue<String> filesToPreview = new LinkedBlockingDeque<String>(1024);
BlockingQueue<String> filesToUpload = new LinkedBlockingDeque<String>(1024);
for (int x = 0; x < NUMBER_OF_THREADS; x++) {
workers[x] = new Thread(new Worker(filesToPreview, currentYear, startSignal, doneSignal));
workers[x].start();
}
In your specific case, you could provide each thread its own file name, or a handle on a file. If you want the file names and entries in a chronological sequence, then just start 2 threads, 1 for acquiring data and placing on a queue, with a barrier/limit of 1000 records, and the other thread as a consumer.
the original code creates multiple threads. I am able to create 90 second snippets from over 1000 MP4 videos in about 30 minutes.
Here I am creating a thread per processor, I usually end up with at least 4 threads on my AWS EC2 instance:
/**
* Here we can find out how many cores we have.
* Then make the number of threads NUMBER_OF_THREADS = the number of cores.
*/
NUMBER_OF_THREADS = Runtime.getRuntime().availableProcessors();
System.out.println("Thread Count: "+NUMBER_OF_THREADS);
for (int x = 0; x < NUMBER_OF_THREADS; x++) {
workers[x] = new Thread(new MyClass(param1, param2));
workers[x].start();
}
I've the below piece of code, where there is a progress bar that has to progressed.
public void createFiles(String srcText, String destText, JTextArea outputTextArea, JProgressBar progressBar) {
String zipFilePath = srcText;
String destDirectory = destText;
UnZip unzipper = new UnZip();
File dir = new File(zipFilePath);
File[] files = dir.listFiles();
System.out.println(files.length);
double pBarInt = (double) files.length / 100;
int count = 1;
System.out.println(count);
if (null != files) {
for (int fileIntList = 0; fileIntList < files.length; fileIntList++) {
System.out.println("coun in vlocj " + count);
String ss = files[fileIntList].toString();
if (null != ss && ss.length() > 0) {
try {
if (files[fileIntList].isDirectory())
continue;
unzipper.unzip(zipFilePath + ss.substring(ss.lastIndexOf("\\") + 1, ss.length()), destDirectory,
outputTextArea);
if ((fileIntList + 1) % pBarInt == 0) {
progressBar.setValue(count);
progressBar.update(progressBar.getGraphics());
count += 1;
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
Here the value of files.length is 25.
My question is, since there are 25 files 1% of them would be 2.5%, can i increase my progress bar for every 4 files processed as 10% or can i show 2.5% whenever a file has been processed.
If files.length is greater than 100, i'm able to do it, but unable to understand for files less than 100.
please let me know how can i get this done.
Thanks
You might just have to set the maximum correctly. E.g.:
progressBar.setMaximum(files.length);
To get only the files and not the directories in the first place do:
File[] files = dir.listFiles(new FilenameFilter() {
#Override
public boolean accept(File file, String name) {
return !file.isDirectory();
}
});
I would write the whole bit like this:
File[] files = dir.listFiles(new FilenameFilter() {
#Override
public boolean accept(File file, String name) {
return !file.isDirectory();
}
});
progressBar.setMaximum(files.length);
for (int i = 0; i < files.length; i++) {
File f = files[i];
try {
unzipper.unzip(f.getAbsolutePath(), destDirectory, outputTextArea);
progressBar.setValue(i);
progressBar.update(progressBar.getGraphics());
} catch (Exception ex) {
ex.printStackTrace();
}
}
And you might want to dispatch it in a thread. See the comment on your question.
Not sure why you need this condition (fileIntList + 1) % pBarInt == 0
You should be setting the progress bar count as a percentage of the total files. Try using
progress.setValue(100);
....
int progressBarValue = count * 100/files.length;
progressBar.setValue(progressBarValue);
For accuracy use the correct files.length - ignoring directories.
I got my program to delete files within a specified file, but then I decided for it to delete the entire directory! This is my code so far, it does nothing when pressing the button... (and the button does have an ActionListener on it).
public void actionPerformed(ActionEvent event) {
if (event.getSource().equals(a)) {
int ans = JOptionPane.showConfirmDialog(null, "You're about to premenently delete this account! Are you sure you want to continue?", "Caution!!", JOptionPane.YES_NO_OPTION);
if (ans == JOptionPane.YES_OPTION){
//delete
File directory = new File("FileIO Plug-Ins\\Accounts\\" + user);
deleteDirectory(directory);
}
run();
}
}
public boolean deleteDirectory(File directory) {
if(directory.exists()){
File[] files = directory.listFiles();
if(files != null){
for(int i = 0; i < files.length; i++) {
if(files[i].isDirectory()) {
deleteDirectory(files[i]);
}
else {
System.out.println("deleting: " + files[i].getName());
files[i].delete();
}
}
}
}
return(directory.delete());
}
the for loop I made does indeed find all the files in the specified folder, and the line
System.out.println("deleting: " + files[i].getName());
does also print every file within the 'user' directory, but doesn't delete them. nor does it delete the folder itself.
Please help! any advise or code source would be great!
delete() returns boolean value which you are ignoring.
true - if and only if file or directory was successfully deleted
false - if could not be deleted for some reason
To get the reason, use Files#delete(Path) for deleting the directory, as it gives you exception if the file cannot be deleted due to some reason.
Quoting the JavaDoc for File#delete()
Note that the Files class defines the delete method to throw an
IOException when a file cannot be deleted. This is useful for error
reporting and to diagnose why a file cannot be deleted.
I ran your code in my machine,it's work well.Maybe you don't have permission to delete your target directory.You can test with my code to find out which file's delete operation is failed.
public static boolean deleteDirectory(File directory) {
if(directory.exists()){
File[] files = directory.listFiles();
if(files != null){
for(int i = 0; i < files.length; i++) {
if(files[i].isDirectory()) {
deleteDirectory(files[i]);
}
else {
if(files[i].delete()) {
System.out.println("Successfully delete: " + files[i].getAbsolutePath());
} else {
System.out.println("Failed to delete: " + files[i].getAbsolutePath());
return false;
}
}
}
}
}
if(directory.delete()){
System.out.println("Successfully delete: " + directory.getAbsolutePath());
return true;
} else {
System.out.println("Failed to delete: " + directory.getAbsolutePath());
return false;
}
}
i tried the following program
import java.io.*;
class dr
{
public static void main(String args[])
{
try{
File[] roots = File.listRoots();
for (int index = 0; index < roots.length; index++)
{ //Print out each drive/partition
System.out.println(roots[index].toString());
}
}
catch(Exception e)
{
System.out.println("error " +e);
}
}
}
but in my system floppy drive is not connected
and i am getting a message like the following
" The drive is not ready for use;its door may be open,Please check drive A: and make sure that disk is inserted and that the drive door is closed"
then three options are shown cancel,try again,continue
when i try continue,it works
but how i could avoid that msg
What are you trying to do?
My recommendation would be to use FileSystemView.
It's used something like this:
FileSystemView fsv = FileSystemView.getFileSystemView();
File[] roots = fsv.getRoots();
for (File f: roots) {
System.out.println(fsv.getSystemDisplayName(f);
}
package com.littletutorials.fs;
import java.io.*;
import javax.swing.filechooser.*;
public class DriveTypeInfo
{
public static void main(String[] args)
{
System.out.println("File system roots returned byFileSystemView.getFileSystemView():");
FileSystemView fsv = FileSystemView.getFileSystemView();
File[] roots = fsv.getRoots();
for (int i = 0; i < roots.length; i++)
{
System.out.println("Root: " + roots[i]);
}
System.out.println("Home directory: " + fsv.getHomeDirectory());
System.out.println("File system roots returned by File.listRoots():");
File[] f = File.listRoots();
for (int i = 0; i < f.length; i++)
{
System.out.println("Drive: " + f[i]);
System.out.println("Display name: " + fsv.getSystemDisplayName(f[i]));
System.out.println("Is drive: " + fsv.isDrive(f[i]));
System.out.println("Is floppy: " + fsv.isFloppyDrive(f[i]));
System.out.println("Readable: " + f[i].canRead());
System.out.println("Writable: " + f[i].canWrite());
System.out.println("Total space: " + f[i].getTotalSpace());
System.out.println("Usable space: " + f[i].getUsableSpace());
}
}
}
reference : http://littletutorials.com/2008/03/10/getting-file-system-details-in-java/
When it comes to Windows, this class WindowsAltFileSystemView proposes an alternative based on FileSystemView
This class is necessary due to an annoying bug on Windows NT where instantiating a JFileChooser with the default FileSystemView will cause a "drive A: not ready" error every time.
I grabbed the Windows FileSystemView impl from the 1.3 SDK and modified it so * as to not use java.io.File.listRoots() to get fileSystem roots.
java.io.File.listRoots() does a SecurityManager.checkRead() which causes the OS to try to access drive A: even when there is no disk, causing an annoying "abort, retry, ignore" popup message every time we instantiate a JFileChooser!
So here, the idea is to extends FileSystemView, replacing the getRoots() method with:
/**
* Returns all root partitians on this system. On Windows, this
* will be the A: through Z: drives.
*/
public File[] getRoots() {
Vector rootsVector = new Vector();
// Create the A: drive whether it is mounted or not
FileSystemRoot floppy = new FileSystemRoot("A" + ":" + "\\");
rootsVector.addElement(floppy);
// Run through all possible mount points and check
// for their existance.
for (char c = 'C'; c <= 'Z'; c++) {
char device[] = {c, ':', '\\'};
String deviceName = new String(device);
File deviceFile = new FileSystemRoot(deviceName);
if (deviceFile != null && deviceFile.exists()) {
rootsVector.addElement(deviceFile);
}
}
File[] roots = new File[rootsVector.size()];
rootsVector.copyInto(roots);
return roots;
}
you can use this;
import java.io.File;
class dr
{
public static void main(String args[])
{
File[] roots=File.listRoots();
for(File root:roots)
{
System.out.println(root.getName());
}
}
}