This question already has answers here:
What is object serialization?
(15 answers)
Closed 2 years ago.
I'm trying to make a Client/Server chat application using java. I'm pretty new to using sockets to communicate between applications. I've decided to use ObjectInput/ObjectOutput streams to send objects between the client and server.
I'm trying to send user data to the server when the client connects to the socket. Here is the code.
Server:
private void startServer() {
try {
this.server = new ServerSocket(port);
this.socket = server.accept();
ChatUtils.log("Accepted a new connection!");
this.output = new ObjectOutputStream(socket.getOutputStream());
this.input = new ObjectInputStream(socket.getInputStream());
try {
User user = (User) input.readObject();
ChatUtils.log(user.getDisplayName() + " (" + user.getUsername() + ") has connected!");
} catch (ClassNotFoundException e) {
}
} catch (IOException e) {
e.printStackTrace();
}
}
Client:
public void connectToServer(int port) {
try {
server = new Socket("127.0.0.1", port);
this.port = port;
this.objectOutput = new ObjectOutputStream(server.getOutputStream());
System.out.println("Connected to a server on port " + port + "!");
objectOutput.writeObject(user);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Everything works fine, but I'm looking for some clarification as to how the methods ObjectOutputStream#writeObject() and ObjectInputStream#readObject() work.
When I write the line User user = (User) input.readObject();, it reads the object as a User object. Would this only attempt to convert "User" objects that are send from the client's ObjectOutputStream?
As this method is only called once, can I cast the input stream to other objects if I send those objects to the server from the output stream? Ex: String message = (String) input.readObject();.
What would happen if I sent multiple objects to the server from the output stream at once?
4)In example one, I try to read the "user" object. What happens if there are two or more objects waiting to be read? How do I determine which object is which? Ex:
// Client
public void connectToServer() {
String message = "Hello server!"
User user = new User("John Doe", "jdoe123");
output.writeObject(user);
output.writeObject(message);
}
If someone could answer these questions, that'd be great. Thanks so much!
Every time you call .writeObject, java will take the object you specified and will serialize it.
This process is a hacky, not-recommended strategy.
Java will first attempt to break down the object you passed into its constituent parts. It will do this, hopefully, with some assistance from the class definition (the class that the object is, i.e. the one returned by theObjectWritten.getClass(). any class def that implements Serializable claims to be designed for this and gets some additional help, but the mechanism will try with reflection hacks if you don't.
Then, the constituent parts are sent along the wire (that is, take the object, and any fields that are primitives can just be sent; ObjectOutputStream knows how to send an int intrinsically, for example. Any other types are sent by, in turn, asking THAT object's class to do so). For each object, java also sends the so called 'serial version uid', which is a calculated number and changes any time any so-called signature changes anywhere in the class. It's a combination of the class's package, name, which class it extends, which interfaces it implements, and every name and type of every field (and possibly every name, return type, param types, and exception types thrown for every method).
So, now we have a bundle, consisting of:
The name of the class (e.g. com.foo.elliott.User)
The serialversionUID of the class
the actual data in User. If User contained any non-primitive fields, apply this process recursively.
Then this is all sent across the wire.
Then on receipt, the receiving code will take all that and pack it back into a User object. This will fail, unless the receiving end actually has com.foo.elliott.User on the classpath, and that def has the same serial version UID.
In other words, if you ever update this class, the transport fails unless the 'other side' also updates.
You can manually massage this stuff by explicitly declaring the serialVersionUID, but note that e.g. any created fields just end up being blank, even if the constructor ordinarily would ensure they could never be.
You can also fully manually manage all this by overriding some specific 'voodoo' methods (a method with a specific name. Java is ordinarily not structurally typed, but these relics of 25 years in the past, such as psv main and these methods, are the only structurally typed things in all of java).
In addition, the binary format of this data is more or less 'closed', it is not obvious, not easy to decode, and few libraries exist.
So, the upshot is:
It is a finicky, error ridden process.
Updating anything you serialize is a pain in the behind.
You stand no chance of ever reading this wire protocol with any programming language except java.
The format is neither easy to read, nor easy to work with, nor particularly compact.
This leads to the inevitable conclusion: Don't use ObjectOutputStream.
Instead, use other serialization frameworks that weren't designed 25 years ago, such as JSON or XML marshallers like google's GSON or Jackson.
NB: In addition your code is broken. Whenever you make a resource, you must also close it, and as code may exit before you get there, the only solution is a special construct. This is how to do it:
try (OutputStream out = socket.getOutputStream()) { .. do stuff here .. }
note that no matter how code 'escapes' from the braces, be it normally (run to the end of it), or because you return/break/continue out of it, or an exception is thrown, the resource is closed.
This also means assigning resources (anything that implements AutoClosable, like Socket, InputStream, and OutputStream, does so) to fields is broken, unless you make the class itself an AutoClosable, and whomever makes it, does so in one of these try-with blocks.
Finally, don't catch exceptions unless you can actually handle them, and 'printStackTrace' doesn't count. If you have no idea how to handle it, throw it onwards; declare your methods to 'throws IOException'. main can (and should!) generally be declared as throws Exception. If truly you can't, the 'stand in', forget-about-it correct way to handle this, and update your IDE to generate this instead of the rather problematic e.printStackTrace(), is this:
catch (ThingICantHandleException e) {
throw new RuntimeException("unhandled", e);
}
Not doing so means your code continues whilst the process is in an error state, and you don't want that.
Related
void appendFile() throws IOException{
FileWriter print = new FileWriter(f, true);
String info = GetData.getWord("Write desired info to File");
print.append(" "); //Write Data
print.append(info);
System.out.println("this executes");
print.flush();
print.close();
}
boolean fileExist(){
return f.exists();
}
try{
if (f.fileExist())
f.appendFile();
else {
throw new IOException();
}
}
catch(IOException e) {
e.printStackTrace();
}
I'm not sure if the ecxeption is well handeled? The FileWriter is not going to be created if there is a fileNotFoundException, therefore don't need to be closed. However, is it possible that this code throws another kind of IOException after the file was opened?, and in that case do I need a finally block to close the file.
No.
It doesn't safely close the resource
The general rule is, if you call a constructor of an object that represents a closable resource, or a method that returns one which documents that this counts as 'opening the resource', which often but not always includes new being part of the method name (examples: socket.getInputStream(), Files.newInputStream), then you are responsible for closing it.
The problem is, what if an exception occurs? So, you have to use try/finally, except that's a mouthful, so there's a convenient syntax for this.
The appendFile method should use it; it isn't, that makes it bad code. This is correct:
try (FileWriter print = new FileWriter(f, true)) {
String info = GetData.getWord("Write desired info to File");
print.append(" "); //Write Data
print.append(info);
System.out.println("this executes");
}
Not how it is not neccessary to flush before close (close implies flush), and in this case, not neccessary to close() - the try construct does it for you. It also does it for you if you get out of the {} of the try via a return statement, via control flow (break), or via an exception, or just by running to the } and exiting normally. No matter how - the resource is closed. As it should be.
It throws description-less exceptions
else throw new IOException(); is no good; add a description that explains why the exception happened. throw new IOException("File not found") is better, but throw new FileNotFoundException(f.toString()) is even better: The message should convey useful information and nothing more than that (in other words, throw new IOException("Something went wrong") is horrible, don't do that, that message adds no useful information), should not end in punctuation (throw new IOException("File not found!") is bad), and should throw a type that is most appropriate (if the file isn't there, FileNotFoundException, which is a subtype of IOException, is more appropriate, obviously).
It commits the capital offense
You should not ever write a catch block whose contents are just e.printStackTrace();. This is always wrong.
Here's what you do with a checked exception:
First, think of what the exception means and whether the nature of your method inherently implies that this exception can occur (vs. that it is an implementation detail). In this case, you didn't show us what the method containing that try/catch stuff even does. But let's say it is called appendFile, obviously a method that includes the text 'file' does I/O, and therefore, that method should be declared with throws IOException. It's not an implementation detail that a method named appendFile interacts with files. It is its nature.
This is somewhat in the eye of the beholder. Imagine a method named saveGame. That's less clear; perhaps the mechanism to save may involve a database instead, in which case SQLException would be normal. That's an example of a method where 'it interacts with the file system' is an implementation detail.
The problem that the exception signals is logical, but needs to be more abstract.
See the above: A save file system can obviously fail to save, but the exact nature of the error is abstract: If the save file system is DB-based, errors would show up in the form of an SQLException; if a file system, IOException, etcetera. However, the idea that saving may fail, and that the code that tried to save has a reasonable chance that it can somewhat recover from this, is obvious. If it's a game, there's a user interface; you should most absolutely TELL the player that saving failed, instead of shunting some stack trace into sysout which they probably never even look at! Telling the user that something failed is one way of handling things, not always the best, but here it applies).
In such cases, make your own exception type and throw that, using the wrapper constructor:
public class SaveException extends Exception {
public SaveException(Throwable cause) {
super(cause);
}
}
// and to use:
public void save() throws SaveException {
try {
try (OutputStream out = Files.newOutputStream(savePath)) {
game.saveTo(out);
}
} catch (IOException e) {
throw new SaveException(e);
}
}
If neither applies, then perhaps the exception is either essentially not handleable or not expectable or nearly always a bug. For example, writing to an outputstream that you know is a ByteArrayOutputStream (which can't throw), trying to load the UTF-8 charset (which is guaranteed by the JVM spec and therefore cannot possibly throw NoSuchCharsetException) - those are not expectable. Something like Pattern.compile("Some-regexp-here") can fail (not all strings are valid regexps), but as the vast majority of regexes in java are literals written by a programmer, any error in them is therefore neccessarily a bug. Those, too, are properly done as RuntimeExceptions (which are exceptions you don't have to catch or list in your throws line). Not handleables are mostly an application logic level thing. All fair game for runtimeexceptions. Make your own or use something that applies:
public void save(int saveSlot) {
if (saveSlot < 1 || saveSlot > 9) throw new IllegalArgumentException("Choose a saveslot from 1 to 9");
// ... rest of code.
}
This really feels like door number one: Whatever method this is in probably needs to be declared as throws IOException and do no catching or trying at all.
Minor nit: Uses old API
There's new API for file stuff in the java.nio.file package. It's 'better', in that the old API does a bunch of bad things, such as returning failure by way of a boolean flag instead of doing it right (by throwing an exception), and the new API has far more support for various bits and bobs of what file systems do, such as support for file links and creation timestamps.
I am trying to send a message from the server to the client using the following method:
final public void sendToClient(Object msg) throws IOException
{
if (clientSocket == null || output == null)
throw new SocketException("socket does not exist");
output.writeObject(msg);
this.output.flush();
}
and before the
output.writeObject;
everything seems fine, but right after, the socket along with the input/output streams all become null..
Any idea why?
Edit:
Here is a link to all server-client parts of the code-
https://drive.google.com/open?id=0B02W0JiTbQUXS1RCVk9ORER3YUU
And when I wrote that the variables become null, what I mean is that there is no exception thrown (tried surrounding it with try-catch) and when I debug, it just changes after said specific line..
Some ideas:
somewhere else in your program the fields are being set to null;
the fields are not becoming null, another set of fields, that were not initialized, is being checked/causing the problem (e.g. from wrong instance); or
the java virtual machine is broken or something else very strange is going on.
the msg object, which we don't known what it is, is messing up the socket in its writeObject method
Anyway the posted code doesn't seem to be the cause for the fields becoming null, hard to make better guesses without a Minimal, Complete, and Verifiable example.
I'm still working on the project I already needed a bit of help with:
JavaFX - TableView doesn't update items
Now I want to understand how this whole Serialization process in Java works, because unfortunately, I don't really get it now.
Before I go on, first of all, I'm a student, I'm not a professional. Second, I'm neither familiar with using DBs, nor XML or JSON, so I'd just like to find solution to my approach, no matter how inelegant it might be in the end, it just needs to work. So please don't feel offended if I just reject any advice in using other techniques.
So here's what I want:
Saving three different class objects to separate files BUT maintaining backward compatibility to each of it. The objects are Settings, Statistics and a "database" object, containing all words in a list added to it. In the future I may add more statistics or settings, means adding new variables, mostly type of IntegerProperty or DoubleProperty.
Now the question is: is it possible to load old version saved files and then during the process just initiate new variables not found in the old version with just null but keep the rest as it has been saved?
All I know is that the first thing to do so is not to alter the serialVersionUID.
Another thing would be saving the whole Model object (which contains the three objects mentioned before), so I just have to implement stuff for one class instead of three. But how would that work then concerning backward compatibility? I mean the class itself would not change but it's attributes in their own class structure.
Finally, what approach should I go for? And most of all, how do I do this and maintaning backward compatibilty at the same time? I do best with some concrete examples rather than plain theory.
Here are two example methods, if it's of any help. I already have methods for each class to write and read an object.
public static void saveModel(Model model, String destination) throws IOException
{
try
{
fileOutput = new FileOutputStream(destination);
objectOutput = new ObjectOutputStream(fileOutput);
objectOutput.writeObject(model);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (objectOutput != null)
try
{
objectOutput.close();
}
catch (IOException e) {}
if (fileOutput != null)
try
{
fileOutput.close();
}
catch (IOException e) {}
}
}
public static Settings readSettings(String destination) throws IOException, FileNotFoundException
{
Settings s = null;
try
{
fileInput = new FileInputStream(destination);
objectInput = new ObjectInputStream(fileInput);
Object obj = objectInput.readObject();
if (obj instanceof Settings)
{
s = (Settings)obj;
}
}
catch (IOException e)
{
e.printStackTrace();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
finally
{
if (objectInput != null) try { objectInput.close(); } catch (IOException e) {}
if (fileInput != null) try { fileInput.close(); } catch (IOException e) {}
}
return s;
}
Tell me if you need more of my current code.
Thank you in advance!
... you must be this tall
Best advice for Serialisation is to avoid it for application persistence, especially if backwards compatibility is desired property in your application.
Answers
Is it possible to load old version saved files and then during the process just initiate new variables not found in the old version with just null but keep the rest as it has been saved?
Yes. Deserialising objects saved using previous versions of the class into a new version of this class will work only if:
fully qualified name of the class has not changed (same name and package)
previous and current class have exactly the same serialVersionUID; if one of the versions is missing it, it will be calculated as a 'hash' of all fields and methods and upon a mismatch deserialisation will fail.
inheritance hierarchy has not changed for that class (the same ancestors)
no fields have been removed in the new version of the class
no fields have become static
no fields have become transient
I just have to implement stuff for one class instead of three. But how would that work then concerning backward compatibility?
Yes. Providing that all classes of all fields of Model and Model class itself adhere to the rules above.
Finally, what approach should I go for? And most of all, how do I do this and maintaning backward compatibilty at the same time?
Yes, as long as you can guarantee all of the above rules forever, you will be backwards compatible.
I am sure you can appreciate that forever, or even for next year can be very hard to guarantee, especially in software.
This is why people do application persistence using more robust data exchange formats, than binary representation of serialised Java objects.
Raw data for the table, could be saved using anything from CSV file to JSON docs stored as files or as documents in NoSQL database.
For settings / config have a look at Java's Properties class which could store and load properties to and from *.properties or *.xml files or separately have a look at YAML.
Finally for backwards compatibility, have a look at FlatBuffers
The field of application persistence is very rich and ripe, so happy exploring.
I am using Serialization to get persistent storage for my library managing app (I know it is not the right way, but it's the way my professor wants it).
I am using the following code inside my main();
controlador.getBiblioteca().getGestorMaterial().setListaLibros((Modelo.ColeccionLibros) controlador.getSerializador().abrirArchivo("libros.dat"));
My Serializador class has the abrirArchivo("FileName.dat") function (openFile in English).
That function looks like this:
public Object abrirArchivo(String nombreDelArchivo) {
Object retorno = null;
try {
lectorArchivos = new ObjectInputStream(new FileInputStream(
nombreDelArchivo));
retorno = lectorArchivos.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return retorno;
}
Now I am trying to check if the program gets a FileNotFoundException for any of the files. If it does, it should just not deserialize the file and go for the next one: that would just mean there is no book in my library yet.
The problem is, if the line runs, it seems to set my book list using setListaLibros() to null. And whenever I try to access that list, i get a NullPointerException. The list was already initialized as an empty list though, so I just need to leave it alone as long as the "libros.dat" file is not found.
What is the right way to get that done?
I don't know if I understand the problem well. However, as I can see in your code, when an exception FileNotFoundException happens, "retorno" will keep null. That's the reason why you get setListaLibros(null).
And then your list will became null. If you don't want that behavior, you should initialize "retorno" with an empty list instead of null.
You could add a line before this: controlador.getBiblioteca().getGestorMaterial().setListaLibros((Modelo.ColeccionLibros) controlador.getSerializador().abrirArchivo("libros.dat")); which checks if the file exists. If it does not, then it prompts the user. This way, the user knows something went wrong and can act accordingly.
Alternatively, you can make a change in your setListLibros method wherein, if the argument passed is null, then you do not do any assignment.
Personally, I would go with the first option.
As a side note, please break down your code, something like so: controlador.getBiblioteca().getGestorMaterial().setListaLibros((Modelo.ColeccionLibros) controlador.getSerializador().abrirArchivo("libros.dat")) can get hard to read and debug.
There is an aspect that the other answers are not mentioning: why are there no serialized objects when your library is empty?!
What I mean is: you could distinguish between "program runs the first time" (and obviously no serialized data exists) or "program ran before; and thus it fully configures itself from serialized data.
Meaning: "being empty" can be a valid state of a library, too. So another option would be to not use a "special value" (aka "no file with data") to represent that information ... but (de)serialize an empty list.
You could check if the file exist like:
String fileName;
File f1 = new File(fileName);
if (f1.exists()) {
//Do the work
}
This question already has answers here:
How to implement a single instance Java application?
(17 answers)
Closed 2 years ago.
I have a program in Java (with a swing gui), and I would like only 1 instance ever to exist. If it attempted to open another instance of the program I would like the current instance to be brought to the foreground.
How do I do this?
Thanks in advance.
Launch the application using Java Web Start and implement the SingleInstanceService of the JNLP API. Here is a demo. of the SingleInstanceService.
If it attempted to open another instance of the program I would like the current instance to be brought to the foreground.
Hook that up in the newActivation(String[]) method of the SingleInstanceListener. It will be passed any arguments that were provided for the new launch. The existing instance gets to decide what to do with the new args (e.g. change file, add new tab, ignore..)
You can do it using a ShutDownHook and a lock file , see this simple example .
I think that it is the simplest way ...
There is no prev-instance in Java, but you can create a pid file in the temp (or /var/run) directory. (And make it File.deleteOnExit() to clean it anyway on exit)
To bring the existing window to top, you may notify the program yourself, thru named pipe, unix socket, or java remote method call, etc. A simple & dirty way is to write to a small file, say $TEMP/foobar-app.bring-to-top, and the program should periodically poll this small file, if it comes to exist, bring the window to top and remove this small file.
I guess Java couldn't handle signals, i.e., kill -HUP PID may not work for Java applications. Even if it could, not every OS have signals.
I did this once with a Socket and a ServerSocket:
First, when you start your application, make a ServerSocket listen on some port, for example 4004. The trick is to check whether it throws an IOException. If it does, there either is another application running or the port is used by another application (check this list for commonly used ports; Note that TCP and UDP ports are not blocking each other), otherwise you can continue with your application startup. If an instance is currently running, you might want to notify it by connecting a TCP Socket (which guarantees that your connection arrives; UDP doesn't).
Here is an example:
ServerSocket ss = null;
try {
ss = new ServerSocket(4004);
} catch (IOException ex0) {
// Port either occupied by your application or a foreign one
// -> Connect
Socket s = null;
try {
s = new Socket();
} catch (Exception ex1) {
// Something went wrong
}
if (s != null) {
// Send some singnal
}
}
if (ss == null) {
// Close or do something else
}
(I wrote this out of my memory, so some things might be wrong or could be done better).
In C# you usually create a Mutex at Applicaiton start. If you cannot create/get it, another instance of the application is already running. Unfortunately I am not 100% sure if this behaves the same in Java or what the exact syntax is.
Hope this helps.
Pattern singletone:
class SingleInstance {
private static SingleInstance instance;
public SingleInstance getInstance() {
if (instance==null)
instance = new SingleInstance();
return instance;
}
private SingleInstance() {
//construct it!
}
}