Asked  11 Months ago    Answers:  5   Viewed   11 times

Update: OK so I've grayed out parts of code and found what was causing the problem. I've added here 3 lines of code with the comment "this is the added code that causes the problem".

But I still don't understand why it affects the result.

I am working on a client-server application that sends data objects via ObjectOutputStream and ObjectInputStream.

I noticed something strange that made me think I might not fully understand object referencing.

On the client side I have a method that creates and returns a User object:

private static User createNewUser()
{
    User newUser = new User();

    newUser.name = "Jim";
    newUser.phone = "054-6885644";
    ..

    return newUser;
}

I create a User object using this method, change one of its properties and send it to the server:

User user = createNewUser();

out.writeObject(user); // this is the added code that causes the problem
out.flush(); // this is the added code that causes the problem

system.out.println("old phone number: " + user.phone); // this prints out 054-6885644
user.phone = "052-9008801";
system.out.println("new phone number: " + user.phone); // this prints out 052-9008801

out.writeObject(user);
out.flush();

On the server side I read the object:

User user = (User) in.readObject(); // this is the added code that causes the problem

User newProfile = (User) in.readObject();
System.out.println("phone number: " + newProfile.phone); // this prints out 054-6885644 (why??)

So, as you can see, before I stream the object, the propery was updated. But after the server deserializes it, it gets the original property value. Why is that?

By the way, I tried cloning the object before streaming it (creating an entirely different object and just copying the fields) and it worked - the propery value did not revert.

So why is this happening? Why does the change to the referenced object's property not saved after streaming?

 Answers

4

When you output an object using writeObject(Object), the instance will be serialized (as expected). The problem is, the serialized version will be cached, and any time you attempt to write that same instance a second time, the cached version of the serialized instance is referenced.

To avoid this, you can either call reset() after calling writeObject(Object), or you could use writeUnshared(Object)

Friday, July 30, 2021
 
Travis
 
4

No, there is no built-in method to do that. The closest to what you want to accomplish is the transferFrom method from FileOutputStream, like so:

  FileChannel src = new FileInputStream(file1).getChannel();
  FileChannel dest = new FileOutputStream(file2).getChannel();
  dest.transferFrom(src, 0, src.size());

And don't forget to handle exceptions and close everything in a finally block.

Friday, June 18, 2021
 
barden
 
3

No, the data is not buffered. A MappedByteBuffer references the data using a pointer. In other words, the data is not copied, it is simply mapped into physical memory. See the API docs if you haven't already.

A memory-mapped file is a segment of virtual memory which has been assigned a direct byte-for-byte correlation with some portion of a file or file-like resource. This resource is typically a file that is physically present on-disk, but can also be a device, shared memory object, or other resource that the operating system can reference through a file descriptor. Once present, this correlation between the file and the memory space permits applications to treat the mapped portion as if it were primary memory.

Source: Wikipedia

If you are going to be reading data quite frequently, it is a good idea to at least cache some of it.

Monday, August 2, 2021
 
1

You need to use:

table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );

Don't use:

tab.setPreferredScrollableViewportSize(new Dimension(1,1));

That is an unrealistic size. That method is to give a reasonable preferred size to the table so that the frame.pack() method will work.

js.setPreferredSize(new Dimension(400,400));

Don't set the preferred size of the scrollpane. The setPreferredScrollableViewportSize() is used to specify a size for the table.

mainPanel.setPreferredSize(new Dimension(500, 500));
mainPanel.setSize(500,500);

Don't set a size or a preferred size of a component. Each component is responsible for determining its own preferred size.

mainPanel=new JPanel();

By default a JPanel uses a FlowLayout which means any component added to it is displayed at its preferred size. I would probably set the layout to a BorderLayout. Then the component can resize with the space available and the scrollbars will be used as required.

Edit:

The scrollbars appear when the preferred size of table is greater than the size of the scrollpane. So you need to set the width of the TableColumn based on the width of the text to be displayed in the column. An easy way to do this is to use the Table Column Adjuster class which will set the width to the largest line of text. YOu would invoke this class after you have added the model (containing the data) to the table:

Updated code:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import java.io.*;

public class TableCreate extends JFrame
{
    JPanel mainPanel;
    TableCreate() throws IOException
    {

        mainPanel=new JPanel(new BorderLayout());
        String InputFile1 = "TableCreate.java";
        BufferedReader breader1 = new BufferedReader(new FileReader(InputFile1));
        String line1 = "";
        line1 = breader1.readLine();

        DefaultTableModel model1 = new DefaultTableModel();
        model1.addColumn("line");

        while((line1=breader1.readLine()) != null)
         {
             System.out.println(line1);
             model1.addRow(new Object[]{line1});
         }
         breader1.close();

        JTable tab=new JTable(model1);

        tab.setPreferredScrollableViewportSize(new Dimension(300, 200));
        tab.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        TableColumnAdjuster tca = new TableColumnAdjuster(tab);
        tca.adjustColumns();

        JScrollPane js = new JScrollPane(tab);
        add(js);
    }

    public static void main(String[] args) throws IOException
    {
        TableCreate tc=new TableCreate();
        tc.pack();
        tc.setVisible(true);
        tc.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
Friday, September 17, 2021
 
fadd
 
1

EDIT: I've added an array in the receiver to close all streams when the receiver stops.

You should redesign your protocol layer. In both devices you have to create a ServerSocket to listen for new Sockets. Obviously, if you call any read() method the current thread will enter in a blocked state, so you need to use a secondary thread. You need to start() and stop() the receiver and use a listener to notify the socket creations. A possible implementation (it can be improved a lot, but the core is this):

Receiver.java

public class Receiver{
private ArrayList<SocketStream> socketStreams;
    private OnNewSocketListener listener;
    private ServerSocket server;
    private Thread thread;
    private int port;

    public static interface OnNewSocketListener{
        void onNewSocket (Stream stream);
    }

    public Receiver (int port, OnNewSocketListener listener){
        this.listener = listener;
            this.port = port;
    }

    public synchronized start (){
        if (thread != null) return;

        server = new ServerSocket (port);
        thread = new Thread (new Runnable (){
                @Override
                public void run (){
                    try{
                        running = true;
                        while (running){
                        socketStreams.add (stream);
                                                    //See Stream.java below
                            listener.onNewSocket (new Stream (server.accept ()));
                        }
                    }catch (SocketException e){
                        //stop() has been called
                    }catch (IOException e){
                        //Error handling
                    }
                }
            }).start ();
        }
    }

    public synchronized void stop (){
        if (thread == null) return;

        running = false;
        try{
            if (server != null){
                server.close ();
            }
        }catch (IOException e){}

    for (SocketStream stream: socketStreams){
        stream.close ();
    }
    socketStreams.clear ();

        thread = null;
    }
}

Then you need another class that starts a thread when you want to read and write to this socket. To read you need another thread. You also need another listener to notify the object read and to notify when the other device closes the stream. You need a startReading() and close() methods (if you stop reading the socket it will close):

Stream.java

public class Stream{
    private Socket socket;
    private OnCloseStreamListener closeListener;
    private OnReadObjectListener readListener;
    private ObjectInputStream in;
    private ObjectOutputStream out;
    private Thread thread;

    public static interface OnReadObjectListener{
        void onReadObject (Object obj);
    }

    public static interface OnCloseStreamListener{
        void onCloseStream ();
    }

    //Used by the receiver to create an input socket
    public Stream (Socket socket){
        this.socket = socket;
        out = new ObjectOutputStream (socket.getOutputStream ());
    }

    //Used by the user to create an output socket, when the client wants to create a socket with the server
    public Stream (String address, int port){
        socket = new Socket (address, port);
        out = new ObjectOutputStream (socket.getOutputStream ());
    }

    public void setOnCloseStreamListener (OnCloseStreamListener listener){
        closeListener = listener;
    }

    public void setOnReadObjectListener (OnReadObjectListener listener){
        readListener = listener;
    }

    public synchronized void startReading (){
        if (thread != null) return;

        thread = new Thread (new Runnable (){
            @Override
            public void run (){
                try{
                    in = new ObjectInputStream (socket.getInputStream ());
                    reading = true;
                    while (reading){
                        Object obj = in.readObject ();
                        if (obj == null){
                            //The other device has closed its socket stream
                            reading = false;
                            closeListener.onCloseSocketStream ();
                        }else{
                            readListener.onReadObject (obj);
                        }
                    }
                }catch (SocketException e){
                    //stopReading() has been called
                }catch (IOException e){
                    //Error handling
                }
            }
        }).start ();
    }

    public synchronized void writeObject (Object obj){
        out.writeObject (obj);
        out.flush;
    }

    public synchronized void close (){
        if (thread != null){
            reading = false;
            socket.close ();
            in.close ();
            out.close ();
            thread = null;
        }else{
            socket.close ();
            in.close ();
        }
    }
}

Usage:
Server

Receiver receiver = new Receiver (5000, new Receiver.OnNewSocketListener (){
    @Override
    void onNewSocket (Stream stream){
        stream.setOnCloseStreamListener (new Stream.OnCloseStreamListener (){
            @Override
            void onCloseStream (){
                //Stream is closed automatically, don't need to call close()
                //Do something
            }
        });
        stream.setOnReadObjectListener (new Stream.OnReadObjectListener (){
            @Override
            void onReadObject (Object obj){
                //Do something with obj
                if (obj.isDoingSomeMaliciousActivities ()){
                    stream.close ();
                }else if (obj.isDoingGoodStuff){
                                    stream.writeObject (new GoodStuff ());
                            }
            }
        });
        stream.startReading ();
    }
});
receiver.start ();
Thread.sleep (10000);
receiver.stop ();

Client

Stream stream = new Stream ("localhost", 5000);
stream.setOnCloseStreamListener (new Stream.OnCloseStreamListener (){
    @Override
    void onCloseStream (){
        //Stream is closed automatically, don't need to call close()
        //Do something
    }
});
stream.setOnReadObjectListener (new Stream.OnReadObjectListener (){
    @Override
    void onReadObject (Object obj){
        //Do something with obj
        if (obj.isDoingSomeMaliciousActivities ()){
            stream.close ();
        }else if (obj.isDoingGoodStuff){
            stream.writeObject (new GoodStuff ());
        }
    }
});
stream.startReading ();
if (iHaveAGoodDay){
    stream.writeObject (new IamHappy ());
}else{
    stream.writeObject (new IwillHackYou ());
}
Thread.sleep (10000);
stream.close ();

This code is the core of the socket layer. It won't work because I don't tested it. You first need to understand the code to proceed with the protocol.
Note: Don't be afraid to use all the listeners you think you need because you are constructing a layer that notifies events. It's like the user interaction when presses a button but with sockets.

Friday, January 7, 2022
 
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :