Java RMI 异常错误

Java RMI Exception Error

我刚开始学习 RMI 和 JFrame,我一直被异常困住了。我正在编写一个 client/server 交互,它将访问雅虎数据库中的股票信息。

这是我的代码:

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();
    }

}

这是我收到的例外情况。

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.run(Unknown Source)
at sun.rmi.transport.Transport.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$run6(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)

堆栈跟踪中报告的根本原因是

java.io.NotSerializableException: stockquote.StockQuote

显然,在您尝试序列化的对象的对象图中,您有一个名为 stockquote.StockQuote 的 class 实例,但 class 不可序列化。

从堆栈跟踪的其他部分来看,序列化尝试似乎与调用远程方法的尝试有关——RMI 依赖于 Java 序列化向远程方法传递参数并接收 return 值。

当然 stockquote.StockQuote 必须实现 java.io.Serializable 如果您希望能够通过 RMI 传递或 return 实例。然而,这可能不足以实际序列化实例,具体取决于 class 的成员。

堆栈跟踪会准确告诉您错误是什么。你需要使 StockQuote 实现 Serializable,因为 RMI 使用 Java 的序列化机制通过线路传输数据(例如方法参数和 return 值)。