3

I am just starting to learn RMI and JFrame and I have been stuck on an exception for a bit. I'm writing a client/server interaction that will access stock information from yahoo's database.

Here's my code:

package stockquote;

public class StockQuote{

    public double currentPrice, priceChange, dailyLow, dailyHigh;

    public StockQuote(double price, double change, double low, double high){
        currentPrice = price;
        priceChange = change;
        dailyLow = low;
        dailyHigh = high;
    }

    public StockQuote(){
        currentPrice = 0;
        priceChange = 0;
        dailyLow = 0;
        dailyHigh = 0;
    }
}



package stockquote;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface StockQuoteInterface extends Remote{
    public StockQuote getQuote(String symbol) throws RemoteException;
}



package stockquote;

import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.UnicastRemoteObject;

import java.io.*;
import java.util.Properties;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.rmi.RemoteException;
import java.util.*;

public class StockQuoteServer implements StockQuoteInterface{

    public StockQuote getQuote(String symbol) throws RemoteException{

        StockQuote information = new StockQuote();

        try{
            URL url = new URL("http://download.finance.yahoo.com/d/quotes.csv?s=" + symbol + "&f=l1c1hg");
            URLConnection conn = url.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String quoteString = in.readLine();
            in.close();

            String[] data = quoteString.split(",");

            information.currentPrice = Double.parseDouble(data[0]);
            information.priceChange = Double.parseDouble(data[1]);
            information.dailyHigh = Double.parseDouble(data[2]);
            information.dailyLow = Double.parseDouble(data[3]);

        }catch(Exception e){
            e.printStackTrace();
        }

        return information;
    }

    public static void main(String[] args) throws Exception {
        try {
            StockQuoteServer obj = new StockQuoteServer();
            StockQuoteInterface stub = (StockQuoteInterface) UnicastRemoteObject.exportObject(obj, 0);

            Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);

            registry.rebind("StockQuoteServer", stub);   
            System.err.println("StockQuote Server is running.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}



package stockquote;

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;
import java.rmi.*;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.*;

public class StockQuoteClient extends JFrame implements ActionListener{
    private static final long serialVersionUID = 1L;

    private static StockQuoteInterface stockQuote;

    private JTextField symbolField = new JTextField(10);
    private JTextField currentPriceField = new JTextField(10);
    private JTextField priceChangeField = new JTextField(10);
    private JTextField dailyHighField = new JTextField(10);
    private JTextField dailyLowField = new JTextField(10);
    private JButton lookup = new JButton("Lookup");

    public StockQuoteClient() throws RemoteException{

        try {
            Registry registry = LocateRegistry.getRegistry("localhost");
            stockQuote = (StockQuoteInterface) registry.lookup("StockQuoteServer");

        } catch (Exception e) {
            e.printStackTrace();
            System.exit(0);
        }

        this.setLayout(new GridLayout(0,1));
        JPanel symbolPanel = new JPanel();
        symbolPanel.add(new JLabel("Stock Symbol: "));
        symbolPanel.add(symbolField);
        this.add(symbolPanel);

        this.setLayout(new GridLayout(0,1));
        JPanel fieldsPanel = new JPanel();
        fieldsPanel.add(new JLabel("Current Price: "));
        fieldsPanel.add(currentPriceField);
        fieldsPanel.add(new JLabel("Price Change: "));
        fieldsPanel.add(priceChangeField);
        fieldsPanel.add(new JLabel("Daily High: "));
        fieldsPanel.add(dailyHighField);
        fieldsPanel.add(new JLabel("Daily Low: "));
        fieldsPanel.add(dailyLowField);
        this.add(fieldsPanel);

        JPanel lookupButtonPanel = new JPanel();
        lookupButtonPanel.add(lookup);
        this.add(lookupButtonPanel);

        lookup.addActionListener(this);

        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setSize(240, 350);
        this.setVisible(true);

    }

    public void actionPerformed(ActionEvent e){ 
        StockQuote quoteInfo = new StockQuote();

        Object source = e.getSource();

        try{
            if(source == lookup){
                String symbol = symbolField.getText().trim();
                quoteInfo = stockQuote.getQuote(symbol);
                if(quoteInfo.currentPrice == 0.0){
                    currentPriceField.setText("Error");
                    priceChangeField.setText("Error");
                    dailyHighField.setText("Error");
                    dailyLowField.setText("Error");
                }else{
                    currentPriceField.setText(Double.toString(quoteInfo.currentPrice));
                    priceChangeField.setText(Double.toString(quoteInfo.priceChange));
                    dailyHighField.setText(Double.toString(quoteInfo.dailyHigh));
                    dailyLowField.setText(Double.toString(quoteInfo.dailyLow));
                }
            }
        }catch(RemoteException ex){
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) throws RemoteException{
        /*StockQuote ggg = new StockQuote();

        Scanner keyboard = new Scanner(System.in);
        System.out.println("Enter a symbol: ");
        String symbol = keyboard.nextLine();

        ggg = stockQuote.getQuote(symbol);

        System.out.println(ggg.currentPrice);*/
        new StockQuoteClient();
    }

}

And here is the exception I am receiving.

Exception in thread "main" java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: stockquote.StockQuote
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at com.sun.proxy.$Proxy0.getQuote(Unknown Source)
at stockquote.StockQuoteClient.main(StockQuoteClient.java:101)
Caused by: java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: stockquote.StockQuote
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at sun.rmi.server.UnicastRef.unmarshalValue(Unknown Source)
... 5 more
Caused by: java.io.NotSerializableException: stockquote.StockQuote
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at sun.rmi.server.UnicastRef.marshalValue(Unknown Source)
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$256(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
GitaarLAB
  • 14,536
  • 11
  • 60
  • 80
Nicholas
  • 918
  • 1
  • 8
  • 14

2 Answers2

4

The stacktrace tells you exactly what the error is. You need make StockQuote impement Serializable, because RMI uses Java's serialization mechanism to transmit data (such as method arguments and return value) over the wire.

Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
2

The root cause reported in the stack trace is

java.io.NotSerializableException: stockquote.StockQuote

Evidently you have an instance of a class named stockquote.StockQuote in the object graph of an object you are trying to serialize, but that class is not serializable.

From other parts of the stack trace, it appears that the serialization attempt is related to an attempt to invoke a remote method -- RMI relies on Java serialization deliver arguments to remote methods and to receive return values from them.

Certainly stockquote.StockQuote must implement java.io.Serializable if you want to be able to pass or return instances over RMI. That might or might not be sufficient to actually serialize instances, however, depending on the class's members.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157