implement writeObject how? serializable singleton how?

F

Frank Fredstone

I have code that I want to execute only once ever, as a side-effect of
constructing an object. I have put this in a static initializer. But,
if I want to allow the object to be serialized I seem to be stuck,
since the initializer is executed during deserialization.

In the java developer's almanac:

http://www.exampledepot.com/egs/java.io/DeserSingle.html

There's an example of a serializable singleton:

public class MySingleton implements Serializable {
static MySingleton singleton = new MySingleton();

private MySingleton() {
}

// This method is called immediately after an object of this
// class is deserialized.
// This method returns the singleton instance.
protected Object readResolve() {
return singleton;
}
}

If I add code to the constructor, it executes during
deserialization, so that doesn't work for me.

Is there someway to determine within a static initializer or
constructor that this object is being constructed and not being
deserialized? If there were I could execute code only during the one
construction of the object, and not again.

I was thinking that I could implement readObject/writeObject. But, how can
I implement writeObject? How do you represent the fields?

In the oreilly RMI book there is an example of a writeObject method,
but it doesn't explain how you are supposed to represent fields of the
object. I've looked around a bit and found nothing.
 
T

Tom Hawtin

Frank said:
public class MySingleton implements Serializable {
static MySingleton singleton = new MySingleton(); ^private final

private MySingleton() {
}

// This method is called immediately after an object of this
// class is deserialized.
// This method returns the singleton instance.
protected Object readResolve() { ^^^^^^^^^private
return singleton;
}
}

If I add code to the constructor, it executes during
deserialization, so that doesn't work for me.

It shouldn't do. readObject (or a call to
ObjectInputStream.defaultReadObject) takes the place of the constructor.
For this sort of class, it doesn't really matter what the fields are set
to as you are going to dump the deserialised object.
Is there someway to determine within a static initializer or
constructor that this object is being constructed and not being
deserialized? If there were I could execute code only during the one
construction of the object, and not again.

I was thinking that I could implement readObject/writeObject. But, how can
I implement writeObject? How do you represent the fields?

Keep the readResolve (only make it private) and make any instance fields
transient (there is no point serialising them).
In the oreilly RMI book there is an example of a writeObject method,
but it doesn't explain how you are supposed to represent fields of the
object. I've looked around a bit and found nothing.

You aren't interested in the fields.

(take the bit about singletons being evil as read)

Tom Hawtin
 
F

Frank Fredstone

Tom Hawtin said:
It shouldn't do. readObject (or a call to
ObjectInputStream.defaultReadObject) takes the place of the
constructor. For this sort of class, it doesn't really matter what the
fields are set to as you are going to dump the deserialised object.

The constructor is called during deserialization, maybe because of the
assignment to the field singleton. If I put a print statement in the
constructor, it executes during deserialization. If I use the
supposedly redundant "transient static" the constructor is still
called during deserialization. If I use transient only the constructor
is called during deserialization. If I use static final, the
constructor is called during deserialization.
Keep the readResolve (only make it private) and make any instance
fields transient (there is no point serialising them).

There is no change. The constructor is still called during deserialization.
You aren't interested in the fields.

(take the bit about singletons being evil as read)

I did read that article, but I'm not sure how it applies to what I'm
trying to do.

For my purposes, the singleton means "cause a side-effect only
once". I'm trying to find a way to have that behavior survive
deserialization.

The field "singleton" was important because witthout
serialization/deserialization it represents the one and only instance
of the object.(and the one and only time that the constructor was
called).

Any scheme I can think of so far to indicate to the class that it is
being constructed rather than deserialized is executed during
deserializaion also, so I can't distinguish between the two.

What about readObject/writeObject. I think I could get around the
problem by implementing writeObject. How should a writeObject method
represent the state of an object?
 
C

Chris Uppal

Frank said:
The constructor is called during deserialization, maybe because of the
assignment to the field singleton.

Did the singleton instance exist before you started deserialising ? I.e. was
the class MySingleton already loaded ? If not then it'll be loaded as a result
of seeing that class mentioned in the data, and the singleton instance will be
constructed at that time, and so your print() statements would be misleading.

-- chris
 
T

Tom Hawtin

Frank said:
The constructor is called during deserialization, maybe because of the
assignment to the field singleton. If I put a print statement in the
constructor, it executes during deserialization. If I use the
supposedly redundant "transient static" the constructor is still
called during deserialization. If I use transient only the constructor
is called during deserialization. If I use static final, the
constructor is called during deserialization.

Perhaps you are seeing the construction during static (class)
initialisation. When an object is deserialised the constructor of the
most derived non-serialisable class (possibly Object) is invoked by the
serialisation mechanism.

"transient static" doesn't make any sense, as static (class) data is not
serialised.
I did read that article, but I'm not sure how it applies to what I'm
trying to do.

For my purposes, the singleton means "cause a side-effect only
once". I'm trying to find a way to have that behavior survive
deserialization.

Are you trying to store the state of the object? That doesn't really
make any sense. You are making and reading a copy of the singleton
state, so there is more than one of the singletons in existence.
The field "singleton" was important because witthout
serialization/deserialization it represents the one and only instance
of the object.(and the one and only time that the constructor was
called).

That's a static, not an instance, field.
Any scheme I can think of so far to indicate to the class that it is
being constructed rather than deserialized is executed during
deserializaion also, so I can't distinguish between the two.

readResolve is called after an object is constructed through
deserialisation.
What about readObject/writeObject. I think I could get around the
problem by implementing writeObject. How should a writeObject method
represent the state of an object?

Not having writeObject is equivalent to having a writeObject that just
calls ObjectOutputStream.defaultWriteFields. It just writes the
non-transient field data out in a nice fashion. The only interesting
piece of data of a singleton to be serialised is its identity (i.e. its
class).

Tom Hawtin
 
F

Frank Fredstone

Tom Hawtin said:
Perhaps you are seeing the construction during static (class)
initialisation. When an object is deserialised the constructor of the
most derived non-serialisable class (possibly Object) is invoked by
the serialisation mechanism.

So, you and Chris Uppal's post as well, are agreeing that the
constructor is called during deserialization.
Are you trying to store the state of the object? That doesn't really
make any sense. You are making and reading a copy of the singleton
state, so there is more than one of the singletons in existence.

I want an object that has a state that is initialized only once, as a
result of an external side-effect. I also want it to be possible for
that state to be serialized, without the state being "reinitialized"
(in the sense of being intitialized again by a specific block of code)
during deserialization. I want the state (the field values) restored,
without the code that initializes them during construction being
called.

In any case, I found information about writeObject in:

http://java.sun.com/j2se/1.3/docs/guide/serialization/spec/examples.doc1.html

Based on that, I have this mutation of the java developer almanac
serializable singleton that seems to survive deserialization. The
external side-effect is represented by retrieving a system property.

The one instance of the class would be normally be referenced using:

MySingleton.instance();

If it were deserialized, the reference would be faked using:

(MySingleton) objectInputStream.readObject();

import java.io.IOException;
import java.io_ObjectOutputStream;
import java.io_ObjectInputStream;
import java.io_ObjectStreamField;
import java.io.Serializable;

public class MySingleton implements Serializable {
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("instance", MySingleton.class),
new ObjectStreamField("value", String.class)
};
private static MySingleton instance = null;
private String value = null;
private MySingleton() {
System.out.println("Constructing MySingleton");
}
private MySingleton(boolean f) {
System.out.println("Constructing MySingleton f");
System.out.println("Setting value to " +
System.getProperty("abc"));
value = System.getProperty("abc");
}
public String value() { return value; }
public static MySingleton instance() {
if (instance == null) {
instance = new MySingleton(true);
}
return instance;
}
private void writeObject(ObjectOutputStream s) throws IOException {
ObjectOutputStream.PutField fields = s.putFields();
fields.put("instance", instance);
fields.put("value", value);
s.writeFields();
}
private void readObject(ObjectInputStream s) throws IOException {
try {
ObjectInputStream.GetField fields = s.readFields();
instance = (MySingleton) fields.get("instance", null);
value = (String) fields.get("value", null);
} catch (ClassNotFoundException cnfe) {
throw new IOException("" + cnfe);
}
}
}
 
T

Tom Hawtin

Frank said:
So, you and Chris Uppal's post as well, are agreeing that the
constructor is called during deserialization.

The constructor is not called for the deserialised copy. It is called
for the singleton copy.
I want an object that has a state that is initialized only once, as a
result of an external side-effect. I also want it to be possible for
that state to be serialized, without the state being "reinitialized"
(in the sense of being intitialized again by a specific block of code)
during deserialization. I want the state (the field values) restored,
without the code that initializes them during construction being
called.
private static MySingleton instance = null;
public static MySingleton instance() {
if (instance == null) {
instance = new MySingleton(true);
}
return instance;
}
This is not thread safe.
private void readObject(ObjectInputStream s) throws IOException {
try {
ObjectInputStream.GetField fields = s.readFields();
instance = (MySingleton) fields.get("instance", null);

But that doesn't change any current instance of MySingleton to the new
singleton. I suspect that a singleton is really not what you want (it
almost never is).

Anyway, proceeding on with deserialisation mutates the singleton, I
suggest just updating the actual singleton instance, and replacing
references to it in other deserialised object to the real version.

public class MySingleton implements java.io.Serializable {
private static final MySingleton INSTANCE = new MyInstance();
public static getInstance() {
return INSTANCE;
}

private String value;
private MySingleton() {
System.out.println("Constructing MySingleton");
}
public synchronized String getValue() {
return value;
}
public synchronized void setValue(String value) {
this.value = value;
}
private Object readResolve(java.io_ObjectInputStream s) {
// Copy data into real instance.
synchronized (INSTANCE) {
INSTANCE.value = this.value;
}

// Replace serial instance with the real one.
return INSTANCE;
}
}
 
F

Frank Fredstone

Tom Hawtin said:
The constructor is not called for the deserialised copy. It is called
for the singleton copy.

If I don't implement readObject/writeObject:

MySingleton ms = (MySingleton) objectInputStream.readObject();
System.out.println("After deserialization");

prints

Constructing MySingleton
After deserialization

then after:

ms.instance();

it does not print "Constructing MySingleton".

That makes me think that the constructor is being called when it's
deserialized.

I'm not sure if I understand the distinction you are making between
the deserialized copy and the singleton copy.
This is not thread safe.

I should have used:

public static synchronized MySingleton instance() {
....
}
But that doesn't change any current instance of MySingleton to the new
singleton. I suspect that a singleton is really not what you want (it
almost never is).

Is there a way to avoid the possibility of their being multiple copies
of an object due to deserialization?

I should also add a clone method that throws an exception

I'm treating serialization as some ugliness that allows you to violate
the interface of a class, and I want to do my best to make my class
survive that ugliness.
Anyway, proceeding on with deserialisation mutates the singleton, I
suggest just updating the actual singleton instance, and replacing
references to it in other deserialised object to the real version.

public class MySingleton implements java.io.Serializable {
private static final MySingleton INSTANCE = new MyInstance();
public static getInstance() {
return INSTANCE;
}

private String value;
private MySingleton() {
System.out.println("Constructing MySingleton");
}
public synchronized String getValue() {
return value;
}
public synchronized void setValue(String value) {
this.value = value;
}
private Object readResolve(java.io_ObjectInputStream s) {
// Copy data into real instance.
synchronized (INSTANCE) {
INSTANCE.value = this.value;
}

// Replace serial instance with the real one.
return INSTANCE;
}
}

That class does not contain constant values as it's "state" and is not
initialized by a side-effect that must be used to inititialize the
state once and only once, which is what I need.
 
T

Tom Hawtin

Frank said:
That makes me think that the constructor is being called when it's
deserialized.

The constructor of a different object is called during deserialisation.
It doesn't make sense to deserialise a singleton. What you can do is
replace references to a singleton in a serial stream with the real
singleton instance.
Is there a way to avoid the possibility of their being multiple copies
of an object due to deserialization?

Implement readResolve to switch to another copy. (Although this can in
general be hacked to get at the original object.)
I should also add a clone method that throws an exception

Object.clone is protected. So long as the class does not make clone
public, does not implement Cloneable and is unsubclassable, then there
is not a problem. Of course to usefully subclass the singleton, you'd
have to be able to create a copy of it anyway.
I'm treating serialization as some ugliness that allows you to violate
the interface of a class, and I want to do my best to make my class
survive that ugliness.

Serialisation effectively provides a second interface[1] to your class.
It should be quite independent of the normal class' API.

Tom Hawtin

[1] interface as a generic term, not the Java keyword "interface".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,817
Latest member
AdalbertoT

Latest Threads

Top