How to improve the performance of JTextPane when loading large files - java

I want to load a large file(1MB of plain text) contents using JTextPane. It took nearly two minutes to load a large file. I want to load the large file into JTextPane within seconds. If it possible to improve the performance of the JTextPane. My Open action code is available in openActionPerformed() method. Please check it and give me some suggestions. Thank you.
Constructor code:
public class OpenDemo extends javax.swing.JFrame {
JTextPane textPane;
JScrollPane scrollPane;
int i=0;
public OpenDemo() {
initComponents();
textPane=new JTextPane();
}
OpenActionPerformed() method:
private void openActionPerformed(java.awt.event.ActionEvent evt) {
int offset = 0;
FileDialog fd = new FileDialog(OpenDemo.this, "Select File", FileDialog.LOAD);
fd.setVisible(true);
String title;
String path;
Path filePath = null;
File file;
if (fd.getFile() != null) {
path = fd.getDirectory() + fd.getFile();
file=new File(path);
filePath=file.toPath();
title=fd.getFile();
JInternalFrame internalFrame = new JInternalFrame("",true,true);
i++;
internalFrame.setName("Doc "+i);
internalFrame.setTitle(title);
scrollPane=new JScrollPane(textPane);
internalFrame.add(scrollPane);
tp.add(internalFrame);
myOffsetTextField=new JTextField();
List<String> allLines = null;
try {
allLines = Files.readAllLines(filePath, Charsets.UTF_8);
}
catch (MalformedURLException ex) {
Logger.getLogger(OpenDemo.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IOException ex) {
Logger.getLogger(OpenDemo.class.getName()).log(Level.SEVERE, null, ex);
}
try{
offset = Integer.parseInt(myOffsetTextField.getText());
}
catch(NumberFormatException ne){
}
int numberOfLinesToShow = 10000;
int start = Math.min(allLines.size(), offset);
int end = Math.min(allLines.size(), start + numberOfLinesToShow);
List<String> sublist = allLines.subList(start, end);
textPane.setText(Joiner.on('\n').join(sublist));
textPane.setCaretPosition(0);
}
Main method:
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(OpenDemo.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(OpenDemo.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(OpenDemo.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(OpenDemo.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new OpenDemo().setVisible(true);
}
});
}
private javax.swing.JMenu jMenu1;
private javax.swing.JMenuBar jMenuBar1;
private javax.swing.JMenuItem open;
private javax.swing.JTabbedPane tp;
}

For a 1 MB text file it's impossible to take two minutes to load unless it's read from a diskette or alike.
Putting it all into a user interface is a non-sense, nobody can do anything with it. Scrolling using a scroll bar get completely unusable, too. Allow the user to enter a starting offset (in lines), read the file using Files.readLines into a List<String>, and display a few lines only.
Code idea
All non-JDK classes come from Guava.
List<String> allLines = Files.readLines(file, Chatsets.UTF8);
int offset = Integer.parseInt(myOffsetTextField.getText());
int numberOfLinesToShow = 10000;
int start = Math.min(allLines.size(), offset);
int end = Math.min(allLines.size(), start + numberOfLinesToShow);
// a sane-sized list of at most `numberOfLinesToShow` lines
List<String> sublist = allLines.sublist(start, end);
textPane.setText(Joiner.on('\n').join(sublist));

Related

how to make a LONGBLOB image from SQL into a buffered image for displaying in a label

I am trying display an array of bytes from a longblob image in a sql db and then make it into a BufferedImage and scale it to the size of my label, parts I know that work for sure is my SQL statement and the rescaling as I implemented it else where. I don't know if it is actually writing to the variable "image" or being made to icon then bufferedImage. im sure there is a way to make it a buffered image from the start but I am not too advanced with this part of java. any insight is helpful below is my code.
private void picPreviewerActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
String vinNumber = vinInput.getText();
try{
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/newbieswithauctions","root","root");
PreparedStatement ps = con.prepareStatement("SELECT itemImage FROM images WHERE itemVin='"+ vinNumber + "'");
ResultSet rs = ps.executeQuery();
byte[] image = null;
while(rs.next()){
image = rs.getBytes("itemImage");
}
Image img = Toolkit.getDefaultToolkit().createImage(image);
ImageIcon icon = new ImageIcon(img);
Image pic = icon.getImage();
BufferedImage bufferedPic =(BufferedImage) pic;
try{
BufferedImage scaled = getScaledInstance(
bufferedPic, picView.getWidth(), picView.getHeight(), RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
picView.setIcon(new ImageIcon(scaled));
}catch(Exception ex){
}
}
catch(Exception ex)
{
}
}
i have revised the code and i have this button and it will open a new form with the pic but nothing shows up this is the code in the button
private void picPreviewerActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
String vinNumber = vinInput.getText();
try{
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/newbieswithauctions","root","root");
PreparedStatement ps = con.prepareStatement("SELECT itemImage FROM images WHERE itemVin='"+ vinNumber + "'");
ResultSet rs = ps.executeQuery();
byte[] image = null;
while(rs.next()){
image = rs.getBytes("itemImage");
}
InputStream in = new ByteArrayInputStream(image);
bufferedPic = ImageIO.read(in);
ImageIO.write(bufferedPic, "png", new File("C:\\Users\\geluna\\Desktop\\Software Engineering\\NWAGUI-sqlpicsworks\\NWAGUI-sqlpicsworks\\images\\newImage.png"));
}
catch(Exception ex)
{
}
new PicSearchWin().setVisible(true);
}
and in the form i have this in the main method so it populates the pic as soon as it opens but nothing happens but when i make a button in that form and put that code in there it will work when button is pressed. looks so bad. i want it to execute when form is opened. any ideas?
private void closeBtnActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
this.dispose();
}
private void btnActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
try{
BufferedImage NewBufferedPic = ImageIO.read(new File("C:\\Users\\geluna\\Desktop\\Software Engineering\\NWAGUI-sqlpicsworks\\NWAGUI-sqlpicsworks\\images\\newImage.png"));
BufferedImage scaled = getScaledInstance(
NewBufferedPic, pic.getWidth(), pic.getHeight(), RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
pic.setIcon(new ImageIcon(scaled));
}catch(Exception ex){
JOptionPane.showMessageDialog(null,"GAY", "Error ", JOptionPane.ERROR_MESSAGE);
}
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(PicSearchWin.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(PicSearchWin.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(PicSearchWin.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(PicSearchWin.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new PicSearchWin().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton btn;
private javax.swing.JButton closeBtn;
public static javax.swing.JLabel pic;
// End of variables declaration
}
Why do you require BufferedImage?
If you stored a BLOB then try this:
Blob blob = rs.getBlob("itemImage");
byte[] bytes = blob.getBytes(1, (int) blob.length());
Image myImage = Toolkit.getDefaultToolkit().createImage(bytes);
Image scaled = createYourScaledInstance();
picView.setIcon(new ImageIcon(scaled));

Java jFrame Login screen, reading from 2 text files, in netbeans

So I started create this project for my PAT Grade 12 task and I hit a speedbump...
I have a login screen, using a jFrame, in netbeans.
My issue is, that I am using 2 text files to store the data: 1 for usernames and 1 for passwords. They are read in using a Scanner and are being delimited with a #.
The point is, I validate the Username using a while loop, but i am unable to validate the password that correspond with that username in the other text file.
So you can imagine 2 text files:
Username
Password
When the 'Login' button is clicked, it uses a while loop to check to see if username already exists. If it does, continue to the Password, else "Username does not exist". The problem comes in when matching the Password, in the same position, in the other text file.
If anybody can help me, it would make my week and save my project.
Please understand that Netbeans only lets you edit certain areas of code. I try my best to keep my code clean and understandable.
Many Thanks,
SnowyTheVampire
package Battlefield4;
import javax.swing.*;
import java.util.*;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Login extends javax.swing.JFrame
{
String Username;
String Password;
public Login()
{
initComponents();
}
//Can not edit from here on
private void initComponents() {
txtUsername = new javax.swing.JTextField();
txtPassword = new javax.swing.JTextField();
lblPassword = new javax.swing.JLabel();
lblUsername = new javax.swing.JLabel();
btnLogin = new javax.swing.JButton();
btnCreate = new javax.swing.JButton();
jLabel1 = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setSize(new java.awt.Dimension(854, 480));
getContentPane().setLayout(new org.netbeans.lib.awtextra.AbsoluteLayout());
getContentPane().add(txtUsername, new org.netbeans.lib.awtextra.AbsoluteConstraints(320, 150, 210, -1));
getContentPane().add(txtPassword, new org.netbeans.lib.awtextra.AbsoluteConstraints(320, 220, 210, -1));
lblPassword.setText("Password");
getContentPane().add(lblPassword, new org.netbeans.lib.awtextra.AbsoluteConstraints(320, 200, -1, -1));
lblUsername.setText("Username");
getContentPane().add(lblUsername, new org.netbeans.lib.awtextra.AbsoluteConstraints(320, 130, -1, -1));
btnLogin.setText("Login");
btnLogin.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnLoginActionPerformed(evt);
}
});
getContentPane().add(btnLogin, new org.netbeans.lib.awtextra.AbsoluteConstraints(320, 290, -1, -1));
btnCreate.setText("Create");
btnCreate.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnCreateActionPerformed(evt);
}
});
getContentPane().add(btnCreate, new org.netbeans.lib.awtextra.AbsoluteConstraints(460, 290, -1, -1));
jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Battlefield4/Batfield/Background/Login (Small).jpg"))); // NOI18N
jLabel1.setText("jLabel1");
getContentPane().add(jLabel1, new org.netbeans.lib.awtextra.AbsoluteConstraints(0, 0, 860, 480));
pack();
}
//Can not edit before this
private void btnLoginActionPerformed(java.awt.event.ActionEvent evt) {
//This is from where Netbeans lets you edit your code.
try {
File user = new File("C:\\Users\\John1012\\Documents\\NetBeansProjects\\PATProject\\src\\Battlefield4\\Batfield\\Users\\Username.txt"); //To create a universal file for Input & Output
File pass = new File("C:\\Users\\John1012\\Documents\\NetBeansProjects\\PATProject\\src\\Battlefield4\\Batfield\\Users\\Password.txt"); //To create a universal file for Input & Output
user.getParentFile().mkdirs(); //To denote the parent file
pass.getParentFile().mkdirs(); //To denote the parent file
Scanner scUser = new Scanner(user).useDelimiter("#"); //To scan the Username file
Scanner scPass = new Scanner(pass).useDelimiter("#");
Username = txtUsername.getText(); //This gets the user input
Password = txtPassword.getText(); //This gets the user input
int pos = 0; //Indicates the position of the Username in the save file
while(scUser.hasNext())
{
pos = pos + 1;
if(scUser.equals(Username))
{ //This is where it fails
for (int i = 0; i <= 5; i++)
{
while(scPass.hasNext())
{
System.out.print(scPass);
}
}
if(scPass.equals(Password))
{
new Selection().setVisible(true);
this.dispose();
}
else
{
JOptionPane.showMessageDialog(null,"Incorrect Password!");
}
}//This is where it works from. The above code is sketchy
else
{
JOptionPane.showMessageDialog(null,"User Does Not Exist!");
}
scUser.close();
}
}
catch (FileNotFoundException ex)
{
Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IOException ex)
{
Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void btnCreateActionPerformed(java.awt.event.ActionEvent evt) {
//Again this is from where Netbeans lets you edit
try {
File user = new File("C:\\Users\\John1012\\Documents\\NetBeansProjects\\PATProject\\src\\Battlefield4\\Batfield\\Users\\Username.txt"); //To create a universal file for Input & Output
File pass = new File("C:\\Users\\John1012\\Documents\\NetBeansProjects\\PATProject\\src\\Battlefield4\\Batfield\\Users\\Password.txt"); //To create a universal file for Input & Output
user.getParentFile().mkdirs(); //To denote the parent file
pass.getParentFile().mkdirs(); //To denote the parent file
Scanner scUser = new Scanner(user); //To scan the Username file
Username = txtUsername.getText(); //This gets the user input
Password = txtPassword.getText(); //This gets the user input
if(scUser.nextLine().contains(Username)) //This reads the entire line and checks for an indexOf of those characters
{
JOptionPane.showMessageDialog(null,"User Already Exists!");
scUser.close(); //Close the Scanner
}
else
{
scUser.close(); //Close the Scanner
if("".equals(Password) || " ".equals(Password)) //Checks to see whether or not the user entered a password
{
JOptionPane.showMessageDialog(null,"Please Enter A Password!");
}
else
{
PrintWriter pwUser = new PrintWriter(new FileWriter(user,true)); //To Write to the Username file
PrintWriter pwPass = new PrintWriter(new FileWriter(pass,true)); //To Write to the Password file
pwUser.print(Username + "#");
pwPass.print(Password + "#");
pwUser.close();
pwPass.close();
new Selection().setVisible(true);
this.dispose();
}
}
scUser.close();
}
catch (FileNotFoundException ex)
{
Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IOException ex)
{
Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
}
}
//Can no longer edit here
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(Login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(Login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(Login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(Login.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Login().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton btnCreate;
private javax.swing.JButton btnLogin;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel lblPassword;
private javax.swing.JLabel lblUsername;
private javax.swing.JTextField txtPassword;
private javax.swing.JTextField txtUsername;
// End of variables declaration
}
Map all users on startup and then just check the map. Use split to separate the username and password. Check that the inputs are not empty before calling onLoginCorrect()
private static final Logger LOG = LogManager.getLogger();
private final Map<String, String> users = new ConcurrentHashMap<>();
public Login() {
super();
final InputStream resource = this.getClass().getResourceAsStream("/users.txt");
final Scanner scanner = new Scanner(resource);
while (scanner.hasNext()) {
final String input = scanner.nextLine();
final String[] userPw = StringUtils.split(input, ':');
this.users.put(userPw[0], userPw[1]);
}
scanner.close();
IOUtils.closeQuietly(resource);
}
public boolean isLoginCorrect(final String username,final String password) {
final boolean result;
if (StringUtils.equals(this.users.get(username), password)) {
result = true;
} else {
Login.LOG.warn("No such user");
result = false;
}
return result;
}
So I figured out how to do it. Thanks everyone for the help!
I am posting this as a simpler method to #Tobi in hopes to help people that aren't as advanced or need simpler help!
private void btnLoginActionPerformed(java.awt.event.ActionEvent evt) {
try {
File user = new File("src\\Battlefield4\\Batfield\\Users\\Username.txt"); //To create a universal file for Input & Output
File pass = new File("src\\Battlefield4\\Batfield\\Users\\Password.txt"); //To create a universal file for Input & Output
user.getParentFile().mkdirs(); //To denote the parent file
pass.getParentFile().mkdirs(); //To denote the parent file
Scanner scUser = new Scanner(user).useDelimiter("#"); //To scan the Username file
Scanner scPass = new Scanner(pass).useDelimiter("#"); //To scan the password file
username = txtUsername.getText(); //This gets the user input
password = txtPassword.getText(); //This gets the user input
int pos = 0; //Indicates the position of the Username in the save file
boolean loggedIn = false; //Flag to check if it's logged in
while(scUser.hasNext() && scPass.hasNext()) //Runs files in congruency
{
if(scUser.next().equalsIgnoreCase(username) && scPass.next().equals(password))
{
loggedIn = true;
scUser.close();
scPass.close();
new Selection(username).setVisible(true);
this.dispose();
break;
}
}
scUser.close();
scPass.close();
if(loggedIn == false)
{
JOptionPane.showMessageDialog(null,"Incorrect Username or Password!");
}
}
catch (FileNotFoundException ex)
{
Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
}
}

Why do JButtons that open webpages only work the first time one is clicked and then get deactivated?

I have a set of JButtons, each of which opens a separate YouTube video webpage. When first running the program, I can click on any ONE button and get the video page. When I try to get another video page with a button click, it doesn't work - in fact, all the buttons are deactivated. This is the case whether or not I close the video webpage.
How can I keep all the buttons activated? Thanks in advance.
Here's the code for reference. The button links and tags are populated from a text file.
//import statements
public class VideoRecord extends JFrame {
private File videoRecordFile;
public VideoRecord() throws FileNotFoundException {
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new GridLayout(2,2));
setSize(new Dimension(500, 500));
videoRecordFile = new File("videorecord.txt");
getButtons();
pack();
}
public void getButtons() throws FileNotFoundException {
Scanner input = new Scanner(videoRecordFile);
while (input.hasNextLine()) {
Scanner lineInput = new Scanner(input.nextLine());
while (lineInput.hasNext()) {
final String urlString = lineInput.next();
String buttonText = lineInput.next();
JButton btn = new JButton(buttonText);
add(btn);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
URL videoURL = new URL(urlString);
URLConnection videoConnection = videoURL.openConnection();
videoConnection.connect();
openWebpage(videoURL);
}
catch (MalformedURLException mue) {}
catch (IOException ioe) {}
setEnabled(false);
}
});
}
}
}
public static void openWebpage(URI uri) {
Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
try {
desktop.browse(uri);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void openWebpage(URL url) {
try {
openWebpage(url.toURI());
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws FileNotFoundException {
VideoRecord vr = new VideoRecord();
}
}
Take a second to look at you code...
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
URL videoURL = new URL(urlString);
URLConnection videoConnection = videoURL.openConnection();
videoConnection.connect();
openWebpage(videoURL);
}
catch (MalformedURLException mue) {}
catch (IOException ioe) {}
setEnabled(false);
}
});
When you click a button you call setEnabled(false);...
This has actually disable the frame, not the button that was clicked...
Try using ((JButton)e.getSource()).setEnabled(false) instead
Don't throw away you Exceptions blindly, they provide important and useful information that can help solve problems

How to save desktop application state?

I am creating an editor in java. I would like to know how to save an intermediate state in java?
For Example when user wants to save the changes done on the editor, how could it be done and also should be reloaded later.
Eg. powerpoint application is saved as .ppt or .pptx. Later the same .ppt while could be opened for further editions. I hope I am clear with my requirement.
The Preferences API with user preferences; most recently edited files, per file maybe timestamp + cursor position, GUI settings.
To save the contents of JTextPane you can serialize the DefaultStyledDocument of JTextPane in a file using proper way of serialization. And when you want to load the content again you can deserialize the same and display it on the JTextPane . Consider the code given below:
import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class SaveEditor extends JFrame implements ActionListener{
public static final String text = "As told by Wikipedia\n"
+"Java is a general-purpose, concurrent, class-based, object-oriented computer programming language."
+ "It is specifically designed to have as few implementation "
+ "dependencies as possible. It is intended to let application developers write once, run anywhere (WORA), "
+ "meaning that code that runs on one platform does not need to be recompiled to run on another. "
+ "Java applications are typically compiled to bytecode (class file) that can run on any Java virtual "
+ "machine (JVM) regardless of computer architecture. Java is, as of 2012, one of the most popular programming "
+ "languages in use, particularly for client-server web applications, with a reported 10 million users.";
JTextPane pane ;
DefaultStyledDocument doc ;
StyleContext sc;
JButton save;
JButton load;
public static void main(String[] args)
{
try
{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
SaveEditor se = new SaveEditor();
se.createAndShowGUI();
}
});
} catch (Exception evt) {}
}
public void createAndShowGUI()
{
setTitle("TextPane");
sc = new StyleContext();
doc = new DefaultStyledDocument(sc);
pane = new JTextPane(doc);
save = new JButton("Save");
load = new JButton("Load");
JPanel panel = new JPanel();
panel.add(save);panel.add(load);
save.addActionListener(this);load.addActionListener(this);
final Style heading2Style = sc.addStyle("Heading2", null);
heading2Style.addAttribute(StyleConstants.Foreground, Color.red);
heading2Style.addAttribute(StyleConstants.FontSize, new Integer(16));
heading2Style.addAttribute(StyleConstants.FontFamily, "serif");
heading2Style.addAttribute(StyleConstants.Bold, new Boolean(true));
try
{
doc.insertString(0, text, null);
doc.setParagraphAttributes(0, 1, heading2Style, false);
} catch (Exception e)
{
System.out.println("Exception when constructing document: " + e);
System.exit(1);
}
getContentPane().add(new JScrollPane(pane));
getContentPane().add(panel,BorderLayout.SOUTH);
setSize(400, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == save)
{
save();
}
else if (evt.getSource() == load)
{
load();
}
}
private void save()//Saving the contents .
{
JFileChooser chooser = new JFileChooser(".");
chooser.setDialogTitle("Save");
int returnVal = chooser.showSaveDialog(this);
if(returnVal == JFileChooser.APPROVE_OPTION)
{
File file = chooser.getSelectedFile();
if (file != null)
{
FileOutputStream fos = null;
ObjectOutputStream os = null;
try
{
fos = new FileOutputStream(file);
os = new ObjectOutputStream(fos);
os.writeObject(doc);
JOptionPane.showMessageDialog(this,"Saved successfully!!","Success",JOptionPane.INFORMATION_MESSAGE);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
if (fos != null)
{
try
{
fos.close();
}
catch (Exception ex){}
}
if (os != null)
{
try
{
os.close();
}
catch (Exception ex){}
}
}
}
else
{
JOptionPane.showMessageDialog(this,"Please enter a fileName","Error",JOptionPane.ERROR_MESSAGE);
}
}
}
private void load()//Loading the contents
{
JFileChooser chooser = new JFileChooser(".");
chooser.setDialogTitle("Open");
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
int returnVal = chooser.showOpenDialog(this);
if(returnVal == JFileChooser.APPROVE_OPTION)
{
File file = chooser.getSelectedFile();
if (file!= null)
{
FileInputStream fin = null;
ObjectInputStream ins = null;
try
{
fin = new FileInputStream(file);
ins = new ObjectInputStream(fin);
doc = (DefaultStyledDocument)ins.readObject();
pane.setStyledDocument(doc);
JOptionPane.showMessageDialog(this,"Loaded successfully!!","Success",JOptionPane.INFORMATION_MESSAGE);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
if (fin != null)
{
try
{
fin.close();
}
catch (Exception ex){}
}
if (ins != null)
{
try
{
ins.close();
}
catch (Exception ex){}
}
}
}
else
{
JOptionPane.showMessageDialog(this,"Please enter a fileName","Error",JOptionPane.ERROR_MESSAGE);
}
}
}
}

Thread.join does not seem to work in my code; am I using it right?

I have a class Automator that can automate a user. I am specifically having problems setting the system clipboard in windows. The Automator class makes use of the ClipSetThread class, which is a thread that sets the system clipboard. A instance of ClipSetThread takes as input a thread, that if null, it joins with (waits for it to complete).
I feel that I am not calling ClipSetThread right because I still have the errors I have had before in its reliability; prior to the ClipSetThread. This code does not throw any errors when it runs, it works about 2/3 of the time though. Other times it will print 1134, _234, or etc. It seems that the threads are not joining (waiting for) each other, or get skipped.
Code:
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseInputListener;
public class Automator extends Thread implements NativeMouseInputListener
{
Robot rob = null;
TheAppClass theApp = null;
ClipSetThread lastClipSet = null;
boolean doit = false;
boolean settingClip = false;
public void run()
{
try // to make the Global hook
{
GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex){theApp.updateOutput("No Global Keyboard or Mouse Hook");return;}
try // to create a robot (can simulate user input such as mouse and keyboard input)
{
rob = new Robot();
}
catch (AWTException e1) {theApp.updateOutput("The Robot could not be created");return;}
while(true) {}
}
public void setApp(TheAppClass app)
{
theApp = app;
theApp.updateOutput("Succesfully started automator");
}
public void setClip(String arg)
{
ClipSetThread set = new ClipSetThread(theApp, lastClipSet);
lastClipSet = set;
set.setClip(arg);
}
public void DOit()
{
theApp.updateOutput("Starting");
pasteAtCursorLocation("1");
tab(1);
pasteAtCursorLocation("2");
tab(1);
pasteAtCursorLocation("3");
tab(1);
pasteAtCursorLocation("4");
tab(1);
theApp.updateOutput("Complete");
}
public void nativeMouseReleased(NativeMouseEvent e)
{
//System.out.println("Mouse Released: " + e.getButton());
if(doit)
{
DOit();
doit = false;
}
}
public void pasteAtCursorLocation(String text)
{
setClip(text);
rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
theApp.updateOutput("Simulated Paste");
}
public void tab(int numTimes)
{
while(numTimes > 0)
{
rob.keyPress(KeyEvent.VK_TAB);
rob.keyRelease(KeyEvent.VK_TAB);
numTimes--;
theApp.updateOutput("Simulated Tab");
}
}
// Unimplemented
public void nativeMouseClicked(NativeMouseEvent arg0) {}
public void nativeMousePressed(NativeMouseEvent arg0) {}
public void nativeMouseDragged(NativeMouseEvent arg0) {}
public void nativeMouseMoved(NativeMouseEvent arg0) {}
}
ClipSetThread:
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
public class ClipSetThread extends Thread
{
Clipboard sysClip = null;
TheAppClass theApp = null;
public ClipSetThread(TheAppClass app, Thread waitFor)
{
theApp = app;
sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
if(waitFor != null)
{try {waitFor.join();}catch (InterruptedException e) {}}
}
public void setClip(String arg)
{
// Two strings that will hopefully never be on the clipboard
String checkStr1 = "9999999999999";
String checkStr2 = "99999999999999";
// When we read in the clipboard we want to see if we change these strings from the ones they
// will never be, if they do change we read the clipboard successfully
String clipBoardTextBefore = checkStr1;
String clipBoardTextAfter = checkStr2;
// First get a copy of the current system clipboard text
while(true)
{
try
{
Transferable contents = sysClip.getContents(null);
clipBoardTextBefore = (String)contents.getTransferData(DataFlavor.stringFlavor);
}
catch(Exception e)
{
try {Thread.sleep(20);} catch (InterruptedException e1) {}
continue;
}
break;
}
// If we failed to change the string it means we failed to read the text
if(clipBoardTextBefore.equals(checkStr1))
theApp.updateOutput("Could NOT get sysClip text");
else
{
// If we didn't failed to get the current text try to change it
while(true)
{
try{sysClip.setContents(new StringSelection(arg), null);}
catch(Exception e)
{
try {Thread.sleep(20);} catch (InterruptedException e1) {}
continue;
}
break;
}
// Now again check to see the clipboard text
while(true)
{
try
{
Transferable contents = sysClip.getContents(null);
clipBoardTextAfter = (String)contents.getTransferData(DataFlavor.stringFlavor);
}
catch(Exception e)
{
try {Thread.sleep(20);} catch (InterruptedException e1) {}
continue;
}
break;
}
// If we failed to read the clipboard text
if(clipBoardTextAfter.equals(checkStr2))
theApp.updateOutput("Could NOT check if sysClip update was successful");
else
{ // We re-read the clipboard text, see if it changed from the original clipboard text
if(clipBoardTextAfter.equals(checkStr1))
theApp.updateOutput("Could NOT successfully set clipboard text");
else
theApp.updateOutput("Set Clipboard Text:" + arg + "\n");
}
}
}
}
So, firstly, you never call start on the ClipSetThread. You should also check to see if the thread is still alive before joining it.
public class ClipSetThread extends Thread {
Clipboard sysClip = null;
TheAppClass theApp = null;
private String toClipboard;
public ClipSetThread(TheAppClass app, Thread waitFor, String toClipBoard) {
theApp = app;
sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
this.toClipboard = toClipBoard;
// !! Check to see if the thread is also alive before trying to join with it...
if (waitFor != null && waitFor.isAlive()) {
try {
waitFor.join();
} catch (InterruptedException e) {
}
}
}
// You should really put your logic into the `run` method in order to allow
// the code to actually run in a separate thread...otherwise there is no
// point in using a thread....
#Override
public void run() {
// Two strings that will hopefully never be on the clipboard
String checkStr1 = "9999999999999";
String checkStr2 = "99999999999999";
// When we read in the clipboard we want to see if we change these strings from the ones they
// will never be, if they do change we read the clipboard successfully
String clipBoardTextBefore = checkStr1;
String clipBoardTextAfter = checkStr2;
// First get a copy of the current system clipboard text
while (true) {
try {
Transferable contents = sysClip.getContents(null);
clipBoardTextBefore = (String) contents.getTransferData(DataFlavor.stringFlavor);
} catch (Exception e) {
try {
Thread.sleep(20);
} catch (InterruptedException e1) {
}
continue;
}
break;
}
// If we failed to change the string it means we failed to read the text
if (clipBoardTextBefore.equals(checkStr1)) {
theApp.updateOutput("Could NOT get sysClip text");
} else {
// If we didn't failed to get the current text try to change it
while (true) {
try {
sysClip.setContents(new StringSelection(toClipboard), null);
} catch (Exception e) {
try {
Thread.sleep(20);
} catch (InterruptedException e1) {
}
continue;
}
break;
}
// Now again check to see the clipboard text
while (true) {
try {
Transferable contents = sysClip.getContents(null);
clipBoardTextAfter = (String) contents.getTransferData(DataFlavor.stringFlavor);
} catch (Exception e) {
try {
Thread.sleep(20);
} catch (InterruptedException e1) {
}
continue;
}
break;
}
// If we failed to read the clipboard text
if (clipBoardTextAfter.equals(checkStr2)) {
theApp.updateOutput("Could NOT check if sysClip update was successful");
} else { // We re-read the clipboard text, see if it changed from the original clipboard text
if (clipBoardTextAfter.equals(checkStr1)) {
theApp.updateOutput("Could NOT successfully set clipboard text");
} else {
theApp.updateOutput("Set Clipboard Text:" + toClipboard + "\n");
}
}
}
}
}
As per our previous converstaion, it's dangerous to use while (true) {}, it's also wasteful, as it will consume CPU cycles unnecessarily...
public class Automator extends Thread implements NativeMouseInputListener {
// A "locking" object...
private static final Object WAIT_LOCK = new Object();
Robot rob = null;
TheAppClass theApp = null;
ClipSetThread lastClipSet = null;
boolean doit = false;
boolean settingClip = false;
public void run() {
try // to make the Global hook
{
GlobalScreen.registerNativeHook();
} catch (NativeHookException ex) {
theApp.updateOutput("No Global Keyboard or Mouse Hook");
return;
}
try // to create a robot (can simulate user input such as mouse and keyboard input)
{
rob = new Robot();
} catch (AWTException e1) {
theApp.updateOutput("The Robot could not be created");
return;
}
// This is wasteful...
// while (true) {
// }
// Locks do not consume CPU cycles while in the wait state...
synchronized (WAIT_LOCK) {
try {
WAIT_LOCK.wait();
} catch (Exception exp) {
}
}
}
public void dispose() {
// Tell the thread it can terminate...
synchronized (WAIT_LOCK) {
WAIT_LOCK.notify();
}
// This will STOP the current thread (which called this method)
// while the lastClipSet finishes...
if (lastClipSet != null && lastClipSet.isAlive()) {
lastClipSet.join();
}
}
public void setClip(String arg) {
ClipSetThread set = new ClipSetThread(theApp, lastClipSet, arg);
lastClipSet = set;
// You MUST START the thread...
set.start();
}
/*...*/
}
Updated
This code could produce a infinite loop. What happens if the clipboard does not contain a String value??
while(true)
{
try
{
Transferable contents = sysClip.getContents(null);
clipBoardTextBefore = (String)contents.getTransferData(DataFlavor.stringFlavor);
}
catch(Exception e)
{
try {Thread.sleep(20);} catch (InterruptedException e1) {}
continue;
}
break;
}
You tend to do this a lot. I might suggest that you provide some kind of "escape" mechanism to allow it to fail after a number of retries...
boolean successful = false;
int retries = 0;
while (!successful && retries < 20) {
{
try
{
Transferable contents = sysClip.getContents(null);
clipBoardTextBefore = (String)contents.getTransferData(DataFlavor.stringFlavor);
successful = true;
}
catch(Exception e)
{
retries++;
try {Thread.sleep(20);} catch (InterruptedException e1) {}
}
}
Updated with working example
Okay, that was fun. I've put together a (simple) working example. You will want to open a text editor of some kind. When you run the program, you have 5 seconds to make it active ;)
The only basic change I've made is I set added a auto delay between events of 250 milliseconds (see rob.setAutoDelay(250).
Now, you could also place a delay between each key event as well, using Robot#delay, but that's up to you
public class Engine extends Thread {
private Robot rob = null;
private PasteThread lastClipSet = null;
public void setClip(String arg) {
if (lastClipSet != null && lastClipSet.isAlive()) {
try {
lastClipSet.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
PasteThread set = new PasteThread(arg);
lastClipSet = set;
lastClipSet.start();
}
public void pasteAtCursorLocation(String text) {
System.out.println("Paste " + text);
setClip(text);
rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
}
public Engine() throws AWTException {
rob = new Robot();
rob.setAutoDelay(250);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
}
pasteAtCursorLocation("This is a simple test, thanks for watching!");
}
public static void main(String[] args) {
try {
new Engine();
} catch (AWTException ex) {
Logger.getLogger(Engine.class.getName()).log(Level.SEVERE, null, ex);
}
}
public class PasteThread extends Thread {
private String toPaste;
public PasteThread(String value) {
toPaste = value;
}
#Override
public void run() {
Clipboard sysClip = Toolkit.getDefaultToolkit().getSystemClipboard();
System.out.println("Current clipboard contents = " + getClipboardContents(sysClip));
sysClip.setContents(new StringSelection(toPaste), null);
System.out.println("New clipboard contents = " + getClipboardContents(sysClip));
}
public String getClipboardContents(Clipboard clipboard) {
String value = null;
boolean successful = false;
int retries = 0;
while (!successful && retries < 20) {
Transferable contents = clipboard.getContents(null);
if (contents.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
value = (String) contents.getTransferData(DataFlavor.stringFlavor);
successful = true;
} catch (Exception exp) {
retries++;
exp.printStackTrace();
}
} else {
retries++;
}
}
System.out.println(successful + "/" + retries);
return value;
}
}
}
Could you please try to repeat the Paste action with a sleep 1 second in between
public void pasteAtCursorLocation(String text)
{
setClip(text);
rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
theApp.updateOutput("Simulated Paste");
// put in a sleep 1 second here
rob.keyPress(KeyEvent.VK_CONTROL);
rob.keyPress(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_V);
rob.keyRelease(KeyEvent.VK_CONTROL);
theApp.updateOutput("Simulated Paste");
}
It could be that pasting 2x is giving different results. The reason for this strange behavior could the way Windows manages the clipboard. If pasting 2x the clipboard is giving different result then you know that the root cause for this strange behavior is not to find in your code but how Java and Windows work together.

Categories