Pipe transfer blocks when writing object with small pipe size - java

I'm having a bit of problem with an example I'm currently testing. For some reason, the execution blocks when writing at oos.writeObject(new SimpleObject());, despite that fact that the pipe should transfer the data across, even (I'd assume) if it had to do it in smaller operations due to a small pipe size. Anyway, the example succeeds when the pipe size is larger than the object, and fails when the pipe size is smaller than the object. If anyone could shed some light on this, it'd be much appreciated.
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.ByteBuffer;
import org.apache.mina.filter.codec.serialization.ObjectSerializationInputStream;
import org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream;
public class ObjTest4 {
public static void main(String[] args) {
System.out.println("exec1");
int objectsToSend = 10;
int objectsRecvd = 0;
try {
System.out.println("exec2");
PipedOutputStream pos = new PipedOutputStream();
ObjectSerializationOutputStream oos = new ObjectSerializationOutputStream(pos);
PipedInputStream pis = new PipedInputStream(pos, 500);
ObjectSerializationInputStream ois = new ObjectSerializationInputStream(pis);
oos.setMaxObjectSize(2000);
ois.setMaxObjectSize(2000);
while (objectsRecvd < objectsToSend) {
System.out.println("exec3");
oos.writeObject(new SimpleObject());
System.out.println("exec3.1");
oos.flush();
System.out.println("exec3.2");
System.out.println("oisavail: " + ois.available());
Object o = ois.readObject();
if (o != null) {
objectsRecvd++;
System.out.println("o: " + o);
} else {
System.out.println("recvd null");
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
and the class for the object being serialised:
package objtest;
import java.io.Serializable;
import java.util.EnumSet;
public class SimpleObject implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public String moo = "moo";
public EnumSet<EnumTest> set = EnumSet.of(EnumTest.Test);
public String moo2 = "moo2";
public String moo3 = "moo3";
public String moo4 = "moo4_";
{
for (int i = 0; i < 8; i++) {
moo4 += moo4;
}
}
/**
*
*/
public SimpleObject() {
// TODO Auto-generated constructor stub
}
/**
* #return the moo
*/
public String getMoo() {
return moo;
}
/**
* #param moo the moo to set
*/
public void setMoo(String moo) {
this.moo = moo;
}
}
Cheers,
Chris

Sorry, I found the problem -- the Java documentation says not to use piped streams from a single thread, as it may deadlock the thread:
Attempting to use both objects from a
single thread is not recommended, as
it may deadlock the thread.

Related

Serialization issue : de.ruedigermoeller

Trying to get https://github.com/RuedigerMoeller/fast-serialization framework as drop in replacement for my JVM serializer. As a first step.
Here is my serializer:
static FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();
public Serialiser() {
conf.registerClass(MapSerializableForm.class, // etc// ...
}
public byte[] serialise(Object obj) {
byte[] bytes = null;
try {
final ByteArrayOutputStream b = new ByteArrayOutputStream();
final FSTObjectOutput o = new FSTObjectOutput(b);
o.writeObject(obj);
bytes = b.toByteArray();
b.close();
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
public Object deserialize(byte[] bytes) {
Object deser = null;
try {
final ByteArrayInputStream b = new ByteArrayInputStream(bytes);
final FSTObjectInput o = new FSTObjectInput(b);
deser = o.readObject();
b.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return deser;
}
The class that is causing me problems is this one: I'm wrapping JavaFX images with Swing images to help me serialise them. My code does work with the standard Java serializer, but is v. slow. I'm hopeful for improvements, if I can get it working. The result is a nullpointerException during deserialization. It's late, here, but I've tried to be clear.
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
public class SerializableImage implements Serializable {
private static final long serialVersionUID = 7984341875952256208L;
public transient Image image ;
public SerializableImage(Image image) {
this.image=image;
}
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
BufferedImage bufferedImage = ImageIO.read(s); //image comes back null !
image = SwingFXUtils.toFXImage(bufferedImage, null);
}
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", s);
}
public double getWidth() {
return image.getWidth();
}
In the readObject method, (inside the stream "s") I'm able to see an incoming FSTObjectInput with a wrappedStack of one object: this as hoped contains a SerializableImage in the "toRead" field. However, I'm uncertain how to get this out and into my BufferedImage:
BufferedImage bufferedImage = ImageIO.read(s);
Hence, currently, the image comes back null. I tried adding an FSTInput, but couldn't get it to work. Thanks for any help.

Java File Transfer Program Hangs before Executing SwingWorker

I've written a program which uses multicast to discover peers on a local network and allows file transfer between them. It works, except some process of acquiring files/initializing the transfer thread is ridiculously slow. It hangs for about 10-15 seconds, then begins transferring and completes normally:
Transfer.java
My JFrame GUI class. This was done with Netbeans for convenience, so any generated code won't be posted here.
package transfer;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import static javax.swing.JFileChooser.FILES_AND_DIRECTORIES;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Transfer extends javax.swing.JFrame {
private final FileDrop fileDrop;
private Client client;
private final Server server;
private final ClientMulticast clientMulticast;
private final ServerMulticast serverMulticast;
public Transfer() throws IOException {
initComponents();
this.setTitle("Transfer");
peerBox.setEditable(false);
peerBox.removeAllItems();
fileDrop = new FileDrop(backgroundPanel, (java.io.File[] files) -> {
System.out.println(files[0].isDirectory());
if (peerBox.getSelectedIndex() != -1) {
sendLabel.setText("Hello");
try {
client = new Client(sendLabel, files, (Peer) peerBox.getSelectedItem());
//Client property change listener - listens for updates to progress bar
client.addPropertyChangeListener((PropertyChangeEvent evt1) -> {
if (null != evt1.getPropertyName()) switch (evt1.getPropertyName()) {
case "progress":
sendProgressBar.setValue((Integer) evt1.getNewValue());
break;
}
});
client.execute();
} catch (Exception ex) {
System.out.println("Unable to send! IOException in FileDrop call.");
ex.printStackTrace(System.out);
}
} else {
sendLabel.setText("Host not found");
}
});
sendProgressBar.setMaximum(100);
sendProgressBar.setMinimum(0);
receiveProgressBar.setMaximum(100);
receiveProgressBar.setMinimum(0);
server = new Server(receiveLabel);
//Server property change listener - listens for updates to progress bar
server.addPropertyChangeListener((PropertyChangeEvent evt1) -> {
if ("progress".equals(evt1.getPropertyName())) {
receiveProgressBar.setValue((Integer) evt1.getNewValue());
}
});
server.execute();
serverMulticast = new ServerMulticast();
serverMulticast.execute();
clientMulticast = new ClientMulticast(peerBox);
clientMulticast.execute();
}
...GENERATED CODE...
private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {
Transfer guiObject = this;
SwingWorker openFile = new SwingWorker<Void, String>() {
#Override
protected Void doInBackground() throws Exception {
openButton.setEnabled(false);
fileChooser.setFileSelectionMode(FILES_AND_DIRECTORIES);
int returnVal = fileChooser.showOpenDialog(guiObject);
if (returnVal == JFileChooser.APPROVE_OPTION && peerBox.getSelectedIndex() != -1) {
File[] fileArray = fileChooser.getSelectedFiles();
client = new Client(sendLabel, fileArray, (Peer) peerBox.getSelectedItem());
//Client property change listener - listens for updates to progress bar
client.addPropertyChangeListener((PropertyChangeEvent evt1) -> {
if ("progress".equals(evt1.getPropertyName())) {
sendProgressBar.setValue((Integer) evt1.getNewValue());
}
});
client.execute();
//block this swingworker until client worker is done sending
while(!client.isDone()) { }
}
openButton.setEnabled(true);
return null;
}
};
openFile.execute();
}
/**
* #param args the command line arguments
* #throws java.lang.ClassNotFoundException
* #throws java.lang.InstantiationException
* #throws java.lang.IllegalAccessException
* #throws javax.swing.UnsupportedLookAndFeelException
*/
public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
System.setProperty("apple.laf.useScreenMenuBar", "true");
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Transfer");
UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
/* Create and display the form */
java.awt.EventQueue.invokeLater(() -> {
try {
new Transfer().setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Transfer.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
// Variables declaration - do not modify
private javax.swing.JPanel backgroundPanel;
private javax.swing.JFileChooser fileChooser;
private javax.swing.JButton openButton;
private javax.swing.JComboBox<Peer> peerBox;
private javax.swing.JLabel receiveHeaderLabel;
private javax.swing.JLabel receiveLabel;
private javax.swing.JPanel receivePanel;
private javax.swing.JProgressBar receiveProgressBar;
private javax.swing.JLabel sendHeaderLabel;
private javax.swing.JLabel sendLabel;
private javax.swing.JPanel sendPanel;
private javax.swing.JProgressBar sendProgressBar;
// End of variables declaration
}
ClientMutlicast.java
Sends out multicast packets and receives responses - modifies the GUI accordingly. This class is where the errors were.
package transfer;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.swing.JComboBox;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class ClientMulticast extends SwingWorker<Void, Peer> {
private boolean peerPreviouslyFound;
private byte[] sendData, receiveData;
private final DatagramSocket mSocket;
private DatagramPacket receivePacket;
private final JComboBox<Peer> peerBox;
private Peer peer;
private ArrayList<Peer> peerList;
public ClientMulticast(JComboBox<Peer> peerBox) throws SocketException {
peerList = new ArrayList<>();
this.peerBox = peerBox;
mSocket = new DatagramSocket();
mSocket.setBroadcast(true);
mSocket.setSoTimeout(300);
sendData = "CLIENT_MSG".getBytes();
}
#Override
protected Void doInBackground() throws IOException, InterruptedException, InvocationTargetException {
while (true) {
try {
receiveData = new byte[1024];
receivePacket = new DatagramPacket(receiveData, receiveData.length);
peerPreviouslyFound = false;
//send broadcast message
mSocket.send(new DatagramPacket(sendData, sendData.length, InetAddress.getByName("239.255.255.255"), 8888));
//receive response
mSocket.receive(receivePacket);
//don't have to worry about responses from local host because
//server rejects multicast packets from local host
peer = new Peer(receivePacket.getAddress(), receivePacket.getPort(), System.currentTimeMillis());
for (Peer peerList1 : peerList) {
if (peerList1.getIPAddress().equals(peer.getIPAddress())) {
peerList1.setTimestamp(System.currentTimeMillis());
peerPreviouslyFound = true;
break;
}
}
//add to peer list only if reponse is valid, not from local host, and not previously received from this host
if ("SERVER_RESPONSE".equalsIgnoreCase(new String(receivePacket.getData()).trim())
&& !peerPreviouslyFound) {
//publish(peer);
peerBox.addItem(peer);
peerList.add(peer);
}
for (int i = 0; i < peerList.size(); i++) {
//if peer is greater than 5 seconds old, remove from list
if (peerList.get(i).getTimestamp() + 5000 < System.currentTimeMillis()) {
peerBox.removeItemAt(i);
peerList.remove(i);
}
}
} catch (SocketTimeoutException ex) {
for (int i = 0; i < peerList.size(); i++) {
//if peer is greater than 5 seconds old, remove from list
if (peerList.get(i).getTimestamp() + 5000 < System.currentTimeMillis()) {
final int j = i;
SwingUtilities.invokeAndWait(() -> {
peerBox.removeItemAt(j);
peerList.remove(j);
});
}
}
}
TimeUnit.MILLISECONDS.sleep(500);
}//end while
}
#Override
protected void process(List<Peer> p) {
peerBox.addItem(p.get(p.size() - 1));
peerList.add(p.get(p.size() - 1));
}
}
I'm pretty sure the issue is when the FileDrop object within the UI constructor tries to execute the Client SwingWorker using client.execute(), there's a large delay. I can debug it but it doesn't show any issues. Also, I know the issue can't be with my socket.connect() call within Client(), because the print statement immediately before socket.connect() doesn't print until the program has resumes from wherever it's hanging. Any ideas? I'm completely lost.
-- EDIT: All code above works as desired, bugs noted here have been solved. If anyone wants the full source, I'll gladly share it.
I am passing my combo box, called peerBox, to my ClientMuticast class. I was modifying this structure directly, storing peers within. This was blocking the interface thread, as the ClientMulticast class was constantly accessing/changing the combo box.
I changed the code to use the combo box to ONLY display values to the gui and store the discovered peers. An array list was used to copy all the values from the combo box upon each invocation of the ClientMulticast constructor. If a peer is discovered, the combo box is updated via the publish() and process() methods within the ClientMulticast class. Any code placed within process() will be scheduled to execute on the EDT.

Serialization of an arraylist which contains another arraylist

my research on google-search and stackoverflow regarding this problem didn't resolve it.
i'd like to show you a snipped of my Datastructure:
there's a class, called "SequenceHolder" => this one carries an:
ArrayList<SequenceData> data;
within the Sequenceholder, there is a function to call the serialization:
public void writeSequenceList() throws FileNotFoundException, IOException {
FileOutputStream fout = new FileOutputStream(path);
ObjectOutputStream oout = new ObjectOutputStream(fout);
oout.writeObject(data);
oout.close();
fout.close();
}
The class SequenceObject has following fields: (this one is on the top, where i start the serialization)
private ArrayList<SequenceModel> recordedSequenceData;
private String sequenceUrl;
while the SequenceModel is defined like this:
private Object sequenceRawData;
private boolean isProcessedByRequest;
The sequenceRawdata objects are basically two other classes (containing Strings only)!
every class of this "trail" implements the interface "Serializable".
this is the deserialization:
public ArrayList<SequenceData> loadSequenceList() throws FileNotFoundException, IOException, ClassNotFoundException {
FileInputStream fileIn = new FileInputStream(path);
ObjectInputStream in = new ObjectInputStream(fileIn);
this.data = (ArrayList<SequenceData>) in.readObject();
in.close();
fileIn.close();
return data; // load from de-serialization
}
after a deserialization of the SequenceObject, i'll only retrieve the "sequenceUrl", but no recordedSequenceData.
Is there a trick to do this?!
It came just up to my mind, to extend some classes with the ObjectOutputStream and call the writingprocess with "this" explicitly in every class - but yeah, i am not sure if thats a good idead.
What do you mean by "The sequenceRawdata objects are basically two other classes (containing Strings only)!" because when I try to run the same program :
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
class SequenceModel implements Serializable
{
public SequenceModel(Object a, boolean b)
{
sequenceRawData = a;
isProcessedByRequest = b;
}
public String toString()
{
return (String)sequenceRawData + isProcessedByRequest + " SeqModel ";
}
private Object sequenceRawData;
private boolean isProcessedByRequest;
}
class SequenceData implements Serializable
{
public SequenceData(ArrayList<SequenceModel> a, String b)
{
recordedSequenceData = a;
sequenceUrl = b;
}
public String toString()
{
return recordedSequenceData + sequenceUrl + " SeqData ";
}
private ArrayList<SequenceModel> recordedSequenceData;
private String sequenceUrl;
}
class SequenceHolder implements Serializable
{
ArrayList<SequenceData> data;
public String toString()
{
return data + " SeqHol ";
}
public SequenceHolder(ArrayList<SequenceData> a)
{
data = a;
}
public void writeSequenceList() throws FileNotFoundException, IOException {
FileOutputStream fout = new FileOutputStream(Test.file);
ObjectOutputStream oout = new ObjectOutputStream(fout);
oout.writeObject(data);
oout.close();
fout.close();
}
public ArrayList<SequenceData> loadSequenceList() throws FileNotFoundException, IOException, ClassNotFoundException {
FileInputStream fileIn = new FileInputStream(Test.file);
ObjectInputStream in = new ObjectInputStream(fileIn);
this.data = (ArrayList<SequenceData>) in.readObject();
in.close();
fileIn.close();
return data; // load from de-serialization
}
}
public class Test
{
public static File file = new File("abc.txt");
public static void main(String[] args)
{
SequenceModel obj = new SequenceModel("abc", false);
ArrayList list = new ArrayList(); list.add(obj);
SequenceData obh = new SequenceData(list, "str");
ArrayList l2 = new ArrayList();
l2.add(obh);
SequenceHolder obi = new SequenceHolder(l2);
try {
obi.writeSequenceList();
System.out.println(obi.loadSequenceList());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
it is able to serialize and deserialize both and there is no problem.
Output it is printing is : [[abcfalse SeqModel ]str SeqData ] which includes everything as desired.
Please let me know if I am missing anything.

Multithreading, Read multiple files and write to new Files

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test6 implements Runnable {
private File file;
private int totalNumberOfFiles = 0;
private static int nextFile = -1;
private static ArrayList<String> allFilesArrayList = new ArrayList<String>();
private static ExecutorService executorService = null;
public Test6(File file) {
this.file = file;
}
private String readFileToString(String fileAddress) {
FileInputStream stream = null;
MappedByteBuffer bb = null;
String stringFromFile = "";
try {
stream = new FileInputStream(new File(fileAddress));
FileChannel fc = stream.getChannel();
bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
/* Instead of using default, pass in a decoder. */
stringFromFile = Charset.defaultCharset().decode(bb).toString();
} catch (IOException e) {
System.out.println("readFileToString IOException");
e.printStackTrace();
} finally {
try {
stream.close();
} catch (IOException e) {
System.out.println("readFileToString IOException");
e.printStackTrace();
}
}
return stringFromFile;
}
private void toFile(String message, String fileName) {
try {
FileWriter fstream = new FileWriter("C:/Users/Nomi/Desktop/Workspace2/Test6/TestWritten/" + fileName);
System.out.println("printing to file: ".concat(fileName));
BufferedWriter out = new BufferedWriter(fstream);
out.write(message);
out.close();
} catch (Exception e) {
System.out.println("toFile() Exception");
System.err.println("Error: " + e.getMessage());
}
}
// private void listFilesForFolder(final File fileOrFolder) {
// String temp = "";
// if (fileOrFolder.isDirectory()) {
// for (final File fileEntry : fileOrFolder.listFiles()) {
// if (fileEntry.isFile()) {
// temp = fileEntry.getName();
// toFile(readFileToString(temp), "Copy".concat(temp));
// }
// }
// }
// if (fileOrFolder.isFile()) {
// temp = fileOrFolder.getName();
// toFile(readFileToString(temp), "Copy".concat(temp));
// }
// }
public void getAllFilesInArrayList(final File fileOrFolder) {
String temp = "";
System.out.println("getAllFilesInArrayList fileOrFolder.getAbsolutePath()" + fileOrFolder.getAbsolutePath());
if (fileOrFolder.isDirectory()) {
for (final File fileEntry : fileOrFolder.listFiles()) {
if (fileEntry.isFile()) {
temp = fileEntry.getAbsolutePath();
allFilesArrayList.add(temp);
}
}
}
if (fileOrFolder.isFile()) {
temp = fileOrFolder.getAbsolutePath();
allFilesArrayList.add(temp);
}
totalNumberOfFiles = allFilesArrayList.size();
for (int i = 0; i < allFilesArrayList.size(); i++) {
System.out.println("getAllFilesInArrayList path: " + allFilesArrayList.get(i));
}
}
public synchronized String getNextFile() {
nextFile++;
if (nextFile < allFilesArrayList.size()) {
// File tempFile = new File(allFilesArrayList.get(nextFile));
return allFilesArrayList.get(nextFile);
} else {
return null;
}
}
#Override
public void run() {
getAllFilesInArrayList(file);
executorService = Executors.newFixedThreadPool(allFilesArrayList.size());
while(nextFile < totalNumberOfFiles)
{
String tempGetFile = getNextFile();
File tempFile = new File(allFilesArrayList.get(nextFile));
toFile(readFileToString(tempFile.getAbsolutePath()), "Copy".concat(tempFile.getName()));
}
}
public static void main(String[] args) {
Test6 test6 = new Test6(new File("C:/Users/Nomi/Desktop/Workspace2/Test6/Test Files/"));
Thread thread = new Thread(test6);
thread.start();
// executorService.execute(test6);
// test6.listFilesForFolder(new File("C:/Users/Nomi/Desktop/Workspace2/Test6/"));
}
}
The programs' doing what's expected. It goes into the folder, grabs a file, reads it into a string and then writes the contents to a new file.
I would like to do this multi threaded. If the folder has N number of files, I need N number of threads. Also I would like to use executor framework if possible. I'm thinking that there can be a method along this line:
public synchronized void getAllFilesInArrayList() {
return nextFile;
}
So each new thread could pick the next file.
Thank you for your help.
Error:
Exception in thread "Thread-0" java.lang.IllegalArgumentException
at java.util.concurrent.ThreadPoolExecutor.<init>(ThreadPoolExecutor.java:589)
at java.util.concurrent.ThreadPoolExecutor.<init>(ThreadPoolExecutor.java:480)
at java.util.concurrent.Executors.newFixedThreadPool(Executors.java:59)
at Test6.run(Test6.java:112)
at java.lang.Thread.run(Thread.java:662)
Firstly, your approach to the problem will result in more synchronization and race condition worries than seems necessary. A simple strategy to keep your threads from racing would be this:
1) Have a dispatcher thread read all the file names in your directory.
2) For each file, have the dispatcher thread spawn a worker thread and hand off the file reference
3) Have the worker thread process the file
4) Make sure you have some sane naming convention for your output file names so that you don't get threads overwriting each other.
As for using an executor, a ThreadPoolExecutor would probably work well. Go take a look at the javadoc: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.html

Updating the JavaFx Gui with threads and or Tasks

I am creating a chat program that contains a GUI that I have created in the new version of the JavaFx Scene builder. I have a main method that extends application and i have a simpleController (that controls all the buttons, labels, anchorPanes, etc) in my GUI.
Other than that, I have a server application that can receive and send messages. For this purpose, I have created the following simple protocol:
Commands / Description:
1 - Ask for permission to connect and at the same time, ask for a user
ID (server finds out how many users are online and adds the id+1)
2 - Chat, the client sends an ID and a String message (Example: 21
Hello (note all of these are on a separate line))
3 - Disconnects the client.
4 - Shows a list of all the clients online.
5 - Ask who else is online (this is only used when a user is
connecting and he needs to know how many users are online in order to
update the GUI).
10 - Error of all sorts if the server returns the 10 message it means
that the call the client just did was an error or that it could not
be completed!
Using this simple logic, it should be fairly easy for me to make users connect, chat and disconnect. However, it turned out that what should have been a simple task has turned out to be my worst nightmare.
So far my users has no problem connecting to the program and more users can connect at the same time.
Where things start to get tricky is when I want to send and receive messages between server and client.
I do not know how I can update my GUI while using a thread. I have tried to read up on the Task class, but I am unable to see whether this should be used instead of a thread or the thread should have this as a parameter.
Should I create a new class that listens for input and make that class extend thread? OR
Should the thread be running in my simpleController class?
Main
public class Main extends Application{
public static void main(String[] args) throws IOException{
Application.launch(Main.class, (java.lang.String[]) null);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
AnchorPane page = (AnchorPane) FXMLLoader.load(Main.class.getResource("testingBackground.fxml"));
Scene scene = new Scene(page);
primaryStage.setScene(scene);
primaryStage.setTitle("Chatten");
primaryStage.show();
} catch (Exception ex) {
java.util.logging.Logger.getLogger(Main.class.getName()).log(
java.util.logging.Level.SEVERE, null, ex);
}
}
}
simpleController
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Scanner;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import com.sun.glass.ui.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.text.Text;
/*
* evt ret array listen med commands da jeg selv kan styre hvilke commands der kommer ind og ud! og brugeren faktisk
* aldrig selv kan vælge!
*/
public class SimpleController extends Thread implements Initializable{
public Button btn_Connect;
public AnchorPane pictureFrame;
public Socket socket = new Socket();
public PrintWriter pw;
public Scanner input;
public int clientId = 1;
public Client client = new Client(socket, pw, input, clientId);
// options!
public TextField txt_userName;
public TextField textField_chat;
// send button
public Button Send;
/*
* current client that the user i connected with, this client is used to send commands and tell other clients who is connected on
* what "ingame chat persons"
*/
public static int currentClientId;
// chatperson username
public Label lbl_userName2;
public Label lbl_userName3;
public Label lbl_chatPerson2;
public Label lbl_Chatperson1_userName;
//Pictures of chat person
public Label chatPerson3;
public Label chatPerson1;
// chat persons textfield
public TextArea txt_ChatPerson1;
//public TextField txt_ChatPerson1;
public TextField txt_ChatPerson2;
public TextField txt_ChatPerson3;
#Override
public void initialize(URL location, ResourceBundle resources) throws NullPointerException {
try {
client.connect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
pictureFrame.setMaxSize(409, 373);
txt_ChatPerson1.setMinWidth(50);
txt_ChatPerson1.setPrefWidth(50);
txt_ChatPerson1.setMaxWidth(300);
txt_ChatPerson1.setText(" ");
btn_Connect.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) throws NullPointerException {
connectMeWithOthers(1);
}
});
Send.setOnAction(new EventHandler<ActionEvent>() {
// WORK IN PROGReSS!!
#Override
public void handle(ActionEvent event) {
/*
* new line code:
*/
String x = textField_chat.getText();
txt_ChatPerson1.setText(x);
txt_ChatPerson1.setVisible(true);
System.out.println("x" +x);
txt_ChatPerson1.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(
ObservableValue<? extends String> observable,
String oldValue, String newValue) {
// txt_ChatPerson1.setPrefRowCount(5);
txt_ChatPerson1.setPrefWidth(txt_ChatPerson1.getText().length()*7);
//txt_ChatPerson1.setPrefHeight(txt_ChatPerson1.getText().length()*3);
}
});
txt_ChatPerson1.autosize();
client.SendChat(x);
}
});
}
/**
* this method connect the client to the other clients who are online on the server!
* the method calls it self after the user has established connection in order to load the other chat persons online
* if the client is the online user online then it will only load the user
* #param id
*/
protected void connectMeWithOthers(int id) {
try {
int responseId = client.sendCommando(id);
System.out.println(" response id "+responseId);
// finds whom is connected and tries to connect to a spot that is avalibul!
//Response is the ID of the chat persons
switch (responseId) {
case 1:
currentClientId = client.reciveCommando();
client.setClientId(currentClientId);
client.sendString(txt_userName.getText());
connectMeWithOthers(5);
break;
case 5:
int times = client.reciveCommando();
int o = 0;
System.out.println("times: "+times);
while (o != times) {
int j = client.reciveCommando();
System.out.println("j"+ j);
String name = client.reciveString();
System.out.println("Name " +name);
createUser(j, name);
o++;
}
start();
break;
case 10:
System.out.println("Connection fail chat room is full! Please try again later!");
case 8:
start();
break;
default:
break;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void createUser(int j, String reciveChat) {
if (j == 1) {
chatPerson1.setVisible(true);
lbl_Chatperson1_userName.setVisible(true);
lbl_Chatperson1_userName.setText(reciveChat);
}else if (j == 2) {
lbl_chatPerson2.setVisible(true);
lbl_userName2.setVisible(true);
lbl_userName2.setText(reciveChat);
}else if (j == 3){
chatPerson3.setVisible(true);
lbl_userName3.setVisible(true);
lbl_userName3.setText(reciveChat);
}else {
Image img = new Image(getClass().getResourceAsStream("Figur.png"));
Label test2 = new Label("", new ImageView(img));
test2.setLayoutX(50);
test2.setLayoutY(30);
test2.setPrefSize(1000, 1000);
pictureFrame.getChildren().addAll(test2);
test2.setVisible(true);
}
}
/*
* denne metode er en rewrite af run metoden.
*/
public void StartClient(){
ClientListner cl = new ClientListner(client);
Task task = new Task<String>() {
#Override
protected String call() throws Exception {
// TODO Auto-generated method stub
return null;
}
};
Thread t = new Thread(task);
cl.start();
while (true) {
if (cl.recived) {
}
}
}
/*
* Run metoden er brugt til at recive data fra andre users og update GUI'en skal muligvis rewrites!?
*
*/
public void run(){
System.out.println("Thread started");
System.out.println(client.getSocket().isConnected());
ClientListner cl = new ClientListner(client);
while (client.getSocket().isConnected()) {
int key = 10;
if (cl.recived) {
try {
key = client.reciveCommando();
System.out.println("jeg er her");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Key "+key);
switch (key) {
// case 2 er recive chat:
case 2:
// først find ud af hvilket ID der har sendt chatten:
int y = 0;
try {
y = client.reciveCommando();
System.out.println("y" + y);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// derefter få beskeden og send den så ud til resten.
String says = client.reciveChat().toString();
if (y == 1) {
txt_ChatPerson1.setText(client.reciveChat());
}else if (y == 2) {
}else {
chatPerson3.setVisible(true);
txt_ChatPerson3.setVisible(true);
txt_ChatPerson3.setText(client.reciveChat());
}
break;
default:
break;
}
}
}
}
Client
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
public class Client {
// disse var static
public final static int portNumber = 6040;
public Socket socket;
private PrintWriter pw;
private Scanner input;
private int clientId;
/**
* #param args
* #throws IOException
*/
public Client(Socket socket, PrintWriter pw, Scanner input, int clientId){
this.socket = socket;
this.pw = pw;
this.input = input;
this.clientId = clientId;
}
public void connect() throws IOException{
// du kan vælge at bruge inetadressen til at connecte i socketet.
InetAddress adr = InetAddress.getByName("localhost");
socket = new Socket("localhost", portNumber);
input=new Scanner(socket.getInputStream());
pw = new PrintWriter(socket.getOutputStream());
}
/**
* This method sends the message (that the client(chat person) writes to the user)
* #param x
* #throws NullPointerException
* #throws IOException
*/
public void SendChat(String x) throws NullPointerException{
pw.println(2);
pw.flush();
pw.println(SimpleController.currentClientId);
pw.flush();
pw.println(x);
pw.flush();
}
public int sendCommando(int id) throws IOException{
System.out.println("Jeg sender"+ id);
pw.println(id);
pw.flush();
/*
* this part of the program sends a command to the server if the command is 1 then 1 is = Connect.
* the program then ask the server is the server is full or is it ok to connect?
* if the response is not 10 then the program will allow a connection to happen the return type will be the Id of which
* the chat person becomes!
*/
// should the method return 0 the Application will do NOTHING!
switch (id) {
case 1:
int k = reciveCommando();
if (k== 10) {
return 10;
}else if (k < 3) {
System.out.println("returned k" + k);
return k;
}else {
return 10;
}
/*
* Closes the connection with the server!
*/
case 3:
socket.close();
return 0;
case 5:
int y = reciveCommando();
return y;
default:
return 0;
}
}
/*
* this method recives a command from the server! the comands can be found in the ChatCommands.txt
* returns the command as an integer!
*/
public int reciveCommando() throws IOException{
Integer i = input.nextInt();
return i;
}
/**
* Gets a String response from the server. This method i used to create other users and give them the correct username.
*
* #param i
* #return
* #throws IOException
*/
public String getStringResponse(int i) throws IOException {
pw.print(i);
pw.flush();
String x = input.nextLine();
return x;
}
/*
* Work in progress - client getter og setter methoder!
*/
public Socket getSocket(){
return socket;
}
public Scanner getInput(){
return input;
}
public PrintWriter getPw(){
return pw;
}
public int getClientId(){
return clientId;
}
public void setClientId(int i ){
clientId = i;
}
public String reciveChat(){
String x = getInput().next();
return x;
}
public String reciveString(){
String x =input.next();
return x;
}
public void sendString(String x){
pw.println(x);
pw.flush();
}
}*
I am really sorry about the code being kind of messy. The run() method in the simple controller was an attempt to make a thread of the simpleController. However, this did not work as I expected. :(
The main goal of this is basically to make sure that the two people in the chat room can chat together. So, all it has to do is update 1 or 2 textareas.
Please check this out if you have not done that already
http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm
This took me a little time to find so I thought I would document it here.
Platform.runLater(new Runnable(){
#Override
public void run() {
// Update your GUI here.
}
});
This is old but since it was up there in google I would like to point out your best bet is to use
FXObservableList
or
Bean Properties like
DoubleProperty
Which are thread safe.
If you're using Java 8 :
Platform.runLater(() -> {
// Update your GUI here.;
});

Categories

Resources