Java 通过GWT RPC传递类对象时出现问题

Java 通过GWT RPC传递类对象时出现问题,java,google-app-engine,gwt,gwt-rpc,Java,Google App Engine,Gwt,Gwt Rpc,我已经使用和运行了GoogleWebToolkit,我正在尝试对其进行一些基本更改,以便更好地理解RPC框架 我修改了StockServiceImpl服务器端类上的“getStocks”方法,以便它返回Stock对象数组而不是String对象。应用程序可以完美编译,但Google Web Toolkit返回以下错误: “没有com.google.gwt.sample.stockwatcher.server.Stock类型的源代码;您是否忘记继承所需的模块?” 客户端类似乎找不到Stock对象

我已经使用和运行了GoogleWebToolkit,我正在尝试对其进行一些基本更改,以便更好地理解RPC框架

我修改了StockServiceImpl服务器端类上的“getStocks”方法,以便它返回Stock对象数组而不是String对象。应用程序可以完美编译,但Google Web Toolkit返回以下错误:

“没有com.google.gwt.sample.stockwatcher.server.Stock类型的源代码;您是否忘记继承所需的模块?”

客户端类似乎找不到Stock对象的实现,即使该类已导入。以下是我的软件包层次结构的屏幕截图,以供参考:

我怀疑我在web.xml中遗漏了什么,但我不知道它是什么。谁能给我指出正确的方向吗


编辑:忘了提到Stock类是可持久的,因此它需要留在服务器端。

GWT需要.java文件和.class文件。此外,Stock需要位于GWT模块的“客户机”位置。

GWT编译器不知道Stock,因为它不在它查找的位置。您可以将其移动到客户机文件夹,或者如果这样做更有意义,请将其留在原处,创建一个ModuleName.gwt.xml来引用您想要的任何其他类,并从中继承Main.gwt.xml文件

例如:DomainGwt.gwt.xml

<module>
    <inherits name='com.google.gwt.user.User'/>
    <source path="javapackagesabovethispackagegohere"/>
</module>

以及:


经过多次尝试和错误,我终于找到了一种方法来做到这一点。这可能不是最好的方法,但它是有效的。希望这篇文章能为其他人节省很多时间和精力

这些说明假定您已完成和

创建Stock类的客户端实现 关于GWT,有几件事需要记住:

  • 服务器端类可以导入客户端类,但反之亦然(通常)
  • 客户端无法导入任何Google应用程序引擎库(即com.Google.appengine.api.users.User)
  • 由于以上两项的原因,客户端永远无法实现我们在com.google.gwt.sample.stockwatcher.server中创建的Stock类。相反,我们将创建一个名为StockClient的新客户端股票类

    StockClient.java: 修改客户端类以使用StockClient[]而不是String[] 现在,我们对客户机类进行一些简单的修改,以便它们知道RPC调用返回StockClient[],而不是String[]

    StockService.java: StockServiceAsync.java: 除addStock、LoadStock和DisplayStock外,所有其他代码保持不变:

    private void loadStocks() {
        stockService = GWT.create(StockService.class);
        stockService.getStocks(new AsyncCallback<String[]>() {
            public void onFailure(Throwable error) {
                handleError(error);
            }
    
            public void onSuccess(String[] symbols) {
                displayStocks(symbols);
            }
        });
    }
    
    private void displayStocks(String[] symbols) {
        for (String symbol : symbols) {
            displayStock(symbol);
        }
    }
    
    private void addStock() {
        final String symbol = newSymbolTextBox.getText().toUpperCase().trim();
        newSymbolTextBox.setFocus(true);
    
        // Stock code must be between 1 and 10 chars that are numbers, letters,
        // or dots.
        if (!symbol.matches("^[0-9a-zA-Z\\.]{1,10}$")) {
            Window.alert("'" + symbol + "' is not a valid symbol.");
            newSymbolTextBox.selectAll();
            return;
        }
    
        newSymbolTextBox.setText("");
    
        // Don't add the stock if it's already in the table.
        if (stocks.contains(symbol))
            return;
    
        addStock(new StockClient(symbol));
    }
    
    private void addStock(final StockClient stock) {
        stockService.addStock(stock.getSymbol(), new AsyncCallback<Long>() {
            public void onFailure(Throwable error) {
                handleError(error);
            }
    
            public void onSuccess(Long id) {
                stock.setId(id);
                displayStock(stock.getSymbol());
            }
        });
    }
    
    我们需要稍微更改addStock方法,以便返回生成的ID:

    public Long addStock(String symbol) throws NotLoggedInException {
      Stock stock = new Stock(getUser(), symbol);
      checkLoggedIn();
      PersistenceManager pm = getPersistenceManager();
      try {
        pm.makePersistent(stock);
      } finally {
        pm.close();
      }
      return stock.getId();
    }
    
    除getStocks外,所有其他方法保持不变:

    public StockClient[] getStocks() throws NotLoggedInException {
      checkLoggedIn();
      PersistenceManager pm = getPersistenceManager();
      List<StockClient> stockclients = new ArrayList<StockClient>();
      try {
        Query q = pm.newQuery(Stock.class, "user == u");
        q.declareParameters("com.google.appengine.api.users.User u");
        q.setOrdering("createDate");
        List<Stock> stocks = (List<Stock>) q.execute(getUser());
        for (Stock stock : stocks)
        {
           stockclients.add(new StockClient(stock.getId(), stock.getSymbol(), stock.getCreateDate()));
        }
      } finally {
        pm.close();
      }
      return (StockClient[]) stockclients.toArray(new StockClient[0]);
    }
    

    如果您遇到相同的问题,请告诉我。它在Google App Engine中工作的事实似乎表明在托管模式下存在缺陷。

    是的,我们确实需要使用序列化将服务器对象发送到客户端。这些是莫代尔??文件设置无法在客户端使用Stock类

    在您的情况下,您只有一个类Stock,您可以在客户端创建一个StockClient。这很容易。但是如果有人有更多的课程,那么解决方案是什么呢。类似于这个类的属性也是一些其他类型的类

    示例:
    stock.getEOD(date.getHigh()

    getEOD
    将返回另一个具有给定日期的类,该类具有
    getHigh
    方法


    在这样大的情况下该怎么办?我不认为在客户端创建所有实现序列化的类都有好处。然后我们必须在服务器和客户机中编写代码。所有课程两次。

    这里有一个更好的答案:


    基本上,您可以将参数添加到APPNAME.gwt.xml文件中,以便编译器为编译器提供服务器端类的路径。

    我也遇到了同样的问题,“mvn gwt:compile”输出没有太大帮助。 相反,当我尝试部署到tomcat时(通过maven-tomcat插件:mvn-tomcat:deploy),我得到了有用的错误消息

    有几件事我必须解决:

  • 使从客户端发送到服务器的对象实现可序列化
  • 将空参数构造函数添加到同一对象

  • 输入上面rustyshelf的答案

    在本例中,我需要编辑ModuleName.gwt.xml文件并添加以下内容:

    <source path='client'/>
    <source path='shared'/>
    
    
    

    我使用新建->Web应用程序项目向导创建了我的项目,但未选中生成项目示例代码选项。然后我创建了共享包。如果我没有取消选中该选项,那么就已经为我创建了包,并按照上述步骤修改了xml文件

    有一个简单得多的解决方案。如果要将自定义设计类的对象从服务器端发送到客户端,则应在共享包中定义此自定义类

    例如,对于您的案例,您只需将Stock.java类(通过拖放)放入

    com.google.gwt.sample.stockwatcher.shared


    包裹。但是,从您的包层次结构屏幕截图来看,您似乎已删除此共享包。只需重新创建此软件包,并将Stock.java放入其中,然后让游戏开始。

    我很难理解您的答案,rustyshelf-您是说我应该在com.google.gwt.sample.stockwatcher中创建Stock.gwt.xml,然后在stockwatcher.gwt.xml中继承它?我试过了,但似乎没用。我似乎找不到任何关于gwt.xml文件如何工作的文档。请其他人发表评论:如果您希望GAE生成的JDO id(@Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY))为Key或Long,情况如何?这能在客户机中实现吗-
    import com.google.gwt.sample.stockwatcher.client.StockClient;
    
    private void loadStocks() {
        stockService = GWT.create(StockService.class);
        stockService.getStocks(new AsyncCallback<String[]>() {
            public void onFailure(Throwable error) {
                handleError(error);
            }
    
            public void onSuccess(String[] symbols) {
                displayStocks(symbols);
            }
        });
    }
    
    private void displayStocks(String[] symbols) {
        for (String symbol : symbols) {
            displayStock(symbol);
        }
    }
    
    private void addStock() {
        final String symbol = newSymbolTextBox.getText().toUpperCase().trim();
        newSymbolTextBox.setFocus(true);
    
        // Stock code must be between 1 and 10 chars that are numbers, letters,
        // or dots.
        if (!symbol.matches("^[0-9a-zA-Z\\.]{1,10}$")) {
            Window.alert("'" + symbol + "' is not a valid symbol.");
            newSymbolTextBox.selectAll();
            return;
        }
    
        newSymbolTextBox.setText("");
    
        // Don't add the stock if it's already in the table.
        if (stocks.contains(symbol))
            return;
    
        addStock(new StockClient(symbol));
    }
    
    private void addStock(final StockClient stock) {
        stockService.addStock(stock.getSymbol(), new AsyncCallback<Long>() {
            public void onFailure(Throwable error) {
                handleError(error);
            }
    
            public void onSuccess(Long id) {
                stock.setId(id);
                displayStock(stock.getSymbol());
            }
        });
    }
    
    import com.google.gwt.sample.stockwatcher.client.StockClient;
    
    public Long addStock(String symbol) throws NotLoggedInException {
      Stock stock = new Stock(getUser(), symbol);
      checkLoggedIn();
      PersistenceManager pm = getPersistenceManager();
      try {
        pm.makePersistent(stock);
      } finally {
        pm.close();
      }
      return stock.getId();
    }
    
    public StockClient[] getStocks() throws NotLoggedInException {
      checkLoggedIn();
      PersistenceManager pm = getPersistenceManager();
      List<StockClient> stockclients = new ArrayList<StockClient>();
      try {
        Query q = pm.newQuery(Stock.class, "user == u");
        q.declareParameters("com.google.appengine.api.users.User u");
        q.setOrdering("createDate");
        List<Stock> stocks = (List<Stock>) q.execute(getUser());
        for (Stock stock : stocks)
        {
           stockclients.add(new StockClient(stock.getId(), stock.getSymbol(), stock.getCreateDate()));
        }
      } finally {
        pm.close();
      }
      return (StockClient[]) stockclients.toArray(new StockClient[0]);
    }
    
    SEVERE: [1244408678890000] javax.servlet.ServletContext log: Exception while dispatching incoming RPC call
    com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract com.google.gwt.sample.stockwatcher.client.StockClient[] com.google.gwt.sample.stockwatcher.client.StockService.getStocks() throws com.google.gwt.sample.stockwatcher.client.NotLoggedInException' threw an unexpected exception: java.lang.NullPointerException: Name is null
    
    <source path='client'/>
    <source path='shared'/>