Java Serialization of referenced objects is "losing values"? - java

Good day,
I am having an interesting problem that I cannot understand. I have a class called "HomeScreenManager" which maintains a LinkedList<String> called chat. The HomeScreenManager instance lives on the server and is in charge of keeping track of the connections and the chat log. I then created a class called "ProtocolHomeScreen" which is a class with a LinkedList<String> called chat and implements Serializable. When it comes time to update all the clients with the chat, I want to send out a ProtocolHomeScreen. So I create a brand new instance of the ProtocolHomeScreen and I say that it's linked list of strings "gets" the HomeScreen's linked list. So in other words, the linked list that I want to serialize was created in a different class, but I am pointing to it with another instance. The problem is that the data does not get sent, only an empty LinkedList appears on the other side. I've done the debugger line by line and inspected all the variables and right before I call writeObject everything appears to be fine, but then on the client side, when I receive the ProtocolHomeScreen instance, the linked list is empty! (its not null though) Any ideas? Thank you!
public class HomeScreenManager implements ObjectMessageHandler, Serializable
{
protected LinkedList<User> users;
protected LinkedList<String> chat;
protected LinkedList<String> sessions;
public HomeScreenManager()
{
chat = new LinkedList<String>();
users = new LinkedList<User>();
sessions = new LinkedList<String>();
}
protected void handleChat(String message)
{
chat.add(message);
ProtocolHomeScreen p = new ProtocolHomeScreen(this);
for(User u:users)
{
u.connection.WriteObject(p); //At this point in time, p has the correct data, but when I deserialize p, the chat list is empty...
}
}
Then here is the protocol class...
public class ProtocolHomeScreen implements Serializable
{
public LinkedList<String> chat;
public LinkedList<String> players;
public LinkedList<String> sessions;
public ProtocolHomeScreen(HomeScreenManager hms)
{
players = new LinkedList<String>();
for(User u:hms.getUsers())
{
players.add(u.getUsername());
}
sessions = hms.getSessions();
chat = hms.getChat();
chat = new LinkedList<String>();
for(String s: hms.getChat())
{
chat.add(s);
}
}
}
Edit
This is the code on the client side... note that the player's LinkedList comes across just fine, but I think that has something to do with the fact that I created a new instance in the constructor of the ProtocolHomeScreen (on the server side)
else if(obj instanceof ProtocolHomeScreen)
{
ProtocolHomeScreen phs = (ProtocolHomeScreen) obj;
sessionModel.removeAllElements();
playersModel.removeAllElements();
chatOutput.setText("");
for(String s:phs.players)
playersModel.addElement(s);
for(String s:phs.sessions)
sessionModel.addElement(s);
for(String s:phs.chat)
{
chatOutput.setText(chatOutput.getText()+s);
}
}

You need to look at ObjectOutputStream.reset() and why it exists. Any given object is only sent once over an ObjectOutputStream unless you reset() the stream or use writeUnshared().

Related

In Hazelcast jet how can we store IList to normal list as I have to sent it in Response?

I am new to Hazelcast jet and in my application on data I am doing some aggregation and getting data but I want to send that in rest response so how can I change it to normal list?
public class ResponseMessage<T> {
private T responseClassType;
private ResponseMessage() {}
private ResponseMessage(T t) {
this.responseClassType = t;
}
public static <T> ResponseMessage<T> withResponseData(T classType) {
return new ResponseMessage<T>(classType);
}
public static ResponseMessage<Void> empty() {
return new ResponseMessage<>();
}
public T getResponseClassType() {
return responseClassType;
}
public void setResponseClassType(T responseClassType) {
this.responseClassType = responseClassType;
}
}
This is my generic response class and as below I am sending response after all calculations:
public ResponseMessage<?> runProcess(Pipeline pl) {
Map<String, BatchStage<Object>> allBatch = new HashMap<String,BatchStage<Object>>();
allBatch.put(z.get("id").toString(), new SomeCalulation().readSource(pipeline));
BatchStage<Object> h = allBatch.values().iterator().next();
h.writeTo(Sinks.list("abc"));
IList<Object> abc = jetInstance.getList("abc");
List<Object> result = new ArrayList(abc);
abc.destroy();
return ResponseMessage.withResponseData(result);
}
Now this is working but everytime I call rest request it is increasing the list and if I clear the list it is showing blank records, please help how can I convert it to normal list or best way to send response?
It was not working because I was joining it after method call:
runProcess(pl);
job.join(); // so because I am joining it after runProcess not working but if I directly return ResponseMessage.withResponseData(jetInstance.getList("abc")); and then join it will work.
I don't see submitting the pipeline as a job and waiting for the result (job.join()). I suppose you have omitted this from your code sample.
To solve your issue with empty list simply copy the result before destroying the list:
job.join();
IList<Object> abc = jetInstance.getList("abc");
List<Object> result = new ArrayList(abc)
abc.destroy();
return ResponseMessage.withResponseData(result);
Also, the list should have a unique name for each request, otherwise, multiple requests will write to the same list, having unpredictable results.

How to use non-keyed state with Kafka Consumer in Flink?

I'm trying to implement (just starting work with Java and Flink) a non-keyed state in KafkaConsumer object, since in this stage no keyBy() in called. This object is the front end and the first module to handle messages from Kafka.
SourceOutput is a proto file representing the message.
I have the KafkaConsumer object :
public class KafkaSourceFunction extends ProcessFunction<byte[], SourceOutput> implements Serializable
{
#Override
public void processElement(byte[] bytes, ProcessFunction<byte[], SourceOutput>.Context
context, Collector<SourceOutput> collector) throws Exception
{
// Here, I want to call to sorting method
collector.collect(output);
}
}
I have an object (KafkaSourceSort) that do all the sorting and should keep the unordered message in priorityQ in the state and also responsible to deliver the message if it comes in the right order thru the collector.
class SessionInfo
{
public PriorityQueue<SourceOutput> orderedMessages = null;
public void putMessage(SourceOutput Msg)
{
if(orderedMessages == null)
orderedMessages = new PriorityQueue<SourceOutput>(new SequenceComparator());
orderedMessages.add(Msg);
}
}
public class KafkaSourceState implements Serializable
{
public TreeMap<String, SessionInfo> Sessions = new TreeMap<>();
}
I read that I need to use a non-keyed state (ListState) which should contain a map of sessions while each session contains a priorityQ holding all messages related to this session.
I found an example so I implement this:
public class KafkaSourceSort implements SinkFunction<KafkaSourceSort>,
CheckpointedFunction
{
private transient ListState<KafkaSourceState> checkpointedState;
private KafkaSourceState state;
#Override
public void snapshotState(FunctionSnapshotContext functionSnapshotContext) throws Exception
{
checkpointedState.clear();
checkpointedState.add(state);
}
#Override
public void initializeState(FunctionInitializationContext context) throws Exception
{
ListStateDescriptor<KafkaSourceState> descriptor =
new ListStateDescriptor<KafkaSourceState>(
"KafkaSourceState",
TypeInformation.of(new TypeHint<KafkaSourceState>() {}));
checkpointedState = context.getOperatorStateStore().getListState(descriptor);
if (context.isRestored())
{
state = (KafkaSourceState) checkpointedState.get();
}
}
#Override
public void invoke(KafkaSourceState value, SinkFunction.Context contex) throws Exception
{
state = value;
// ...
}
}
I see that I need to implement an invoke message which probably will be called from processElement() but the signature of invoke() doesn't contain the collector and I don't understand how to do so or even if I did OK till now.
Please, a help will be appreciated.
Thanks.
A SinkFunction is a terminal node in the DAG that is your job graph. It doesn't have a Collector in its interface because it cannot emit anything downstream. It is expected to connect to an external service or data store and send data there.
If you share more about what you are trying to accomplish perhaps we can offer more assistance. There may be an easier way to go about this.

How do I add objects to an observable array backing list?

So I have a class called mailbox, which is a collection of messages.
Example of a message, created in a "server" class:
Message message1 = new Message(senderRand, recipRand, subject1, today, body1);
Mailbox class:
public class Mailbox {
public String name;
ObservableList<String> messagelist = FXCollections.observableArrayList();
ListView<String> list = new ListView<String>(messagelist);
public Mailbox (String name) {
this.name = name;
}
public int addMessage(Message message) {
messagelist.add(message);
}
I'm not exactly sure what I'm doing, but the idea is that I have Mailbox objects like an "inbox" that is meant to used to display email Messages objects on a javafx application. Then I need to remove a message from an "inbox" Mailbox and move it to a "trash" Mailbox.
I don't understand how to create this backing array, basically. Should I be extending collection in the Mailbox class? I'm not sure how to format this mailbox class so that I can add/remove Messages from a javafx app. My javafx app is just a list of mailboxes(inbox, important, trash), next to a list of messages for selected mailbox, next to a display of a selected message.
Need mailboxes to be collections of objects. Don't know how.
Solution
You don't need a backing list for your use-case (the FXCollections.observableArrayList() call has already created a list for you).
You need to set the type of the list appropriately (<Message>, not <String>).
Sample Code
public class Mailbox {
private final String name;
private final ObservableList<Message> messages =
FXCollections.observableArrayList();
public Mailbox (String name) {
this.name = name;
}
public void addMessage(Message message) {
messages.add(message);
}
public ObservableList<Message> messages() {
return FXCollections.unmodifiableObservableList(messages);
}
public String getName() {
return name;
}
}
Implementation Note
The above solution uses FXCollections.unmodifiableObservableList() to create and return an unmodifiable wrapper list on top of provided observable list.
return FXCollections.unmodifiableObservableList(messages);
If you it is OK that an external object can modify the messages list directly without interacting further with mailbox methods, then you could just write:
return messages;
If that was the case, then you probably wouldn't need the addMessage method call at all, because a user of the class could just write:
mailbox.messages().add(new Message());

Static ArrayList does not modify in another class

I am working on a client - server application and I have the following problem.
In one class (Server), I have a static reference to an ArrayList called clients and a static getter getClientsArray(). In another class (ServerThread), I use the same ArrayList and I successfully modify it (add / delete clients).
For every client, I call the method getClientsArray() and get that ArrayList. The problem is that the ArrayList is empty here, in Client class. I check it every time I update it in ServerThread (after a client connects) and it should have something in it.
public class Server {
private static ArrayList<User> clients = new ArrayList<User>();
public static ArrayList<User> getClientsArray() {
return clients;
}
and somewhere I call: new ServerThread(sock, users).start();
}
public class ServerThread extends Thread {
private ArrayList<User> users;
public ServerThread(Socket client, ArrayList<User> users) {
this.client = client;
this.users = users;
}
if I modify users in this class, the changes will occur
}
public class Client extends JFrame {
private ArrayList<User> users;
public Client() {
initGraphics();
users = Server.getClientsArray();
System.out.println(users.size()); <- This line always writes 0!!
}
}
There is a simple rule defined by Brian Goetz in JCIP:
If multiple threads access the same mutable state variable without appropriate synchronization, your program is broken.
You have clients mutable state and you don't use any mean of synchronizing access to it.
Static variables are only global to one program instance. Here, you have at least two programs running; You have one server program, and at least one other client program. The clients and server do not share any common heap, or program variables, so the static list will only be seen by that program.
If you want to share the list, you will need to pass it through sockets or some other data transfer. This question, How to transfer objects over network using java, has a code example of how to pass objects through sockets.

GWT and Generics

We are having a SerializationException error when sending a list of objects using RPC and Java Generics.
I'm creating this widget to show the error:
public class Test<T> {
ListDataProvider<T> ldp = new ListDataProvider<T>();
public void setItems(List<T> list){
for(T t :list){
ldp.getList().add(t);
}
}
public List<T> getItems(){
return ldp.getList();
}
}
This is the code for creating the Test widget and passing a list of POJOs (where ExporterFormKey is the POJO object)
List<ExporterFormKey> list = new ArrayList<ExporterFormKey>();
ExporterFormKey key = new ExporterFormKey();
key.setKey("key1");
list.add(key);
Test<ExporterFormKey> test = new Test<ExporterFormKey>();
test.setItems(list);
At the end the next code throws a SerializationException:
service.sendList(test.getList(), new AsyncCallback...);
While the next one does fine:
service.sendList(list, new AsyncCallback...);
-----Edit----
I found that doing the next code also works
List<ExporterFormKey> newList = new ArrayList<ExporterFormKey>();
newList.add(test.getItems().get(0));
service.sendList(newList , new AsyncCallback...);
Or this also works
List<ExporterFormKey> newList = new ArrayList<ExporterFormKey>(test.getItems());
I also found this change on Test works!
public List<T> getItems(){
return new ArrayList<T>(ldp.getList());
}
http://blog.rubiconred.com/2011/04/gwt-serializationexception-on-rpc-call.html
As izaera suggested the ListDataProvider uses a non-serializable list implementation (ListWrapper) which cannot be sent directly across the wire.
Wrapping the response from ListDataProvider's getList() method into a new ArrayList as you have suggested in your post is the simplest way to workaround the issue.

Categories