Проблемы при передаче объектов класса через GWT RPC - PullRequest
20 голосов
/ 06 июня 2009

Я прошел через Google Web Toolkit StockWatcher Tutorial с использованием Eclipse и Google Plugin , и я пытаюсь внести некоторые основные изменения это так, чтобы я мог лучше понять структуру RPC.

Я изменил метод getStocks в серверном классе StockServiceImpl, чтобы он возвращал массив объектов Stock вместо объектов String. Приложение отлично компилируется, но Google Web Toolkit возвращает следующую ошибку:

"Исходный код для типа com.google.gwt.sample.stockwatcher.server.Stock недоступен; вы забыли унаследовать необходимый модуль?"

Google Web Toolkit Hosted Mode

Кажется, что классы на стороне клиента не могут найти реализацию объекта Stock, даже если класс был импортирован. Для справки вот скриншот моей иерархии пакетов:

Eclipse Package Hierarchy

Я подозреваю, что мне чего-то не хватает в web.xml, но я понятия не имею, что это такое. Кто-нибудь может указать мне правильное направление?

РЕДАКТИРОВАТЬ: Забыл упомянуть, что класс Stock является постоянным, поэтому он должен оставаться на стороне сервера.

Ответы [ 8 ]

23 голосов
/ 08 июня 2009

После долгих проб и ошибок мне удалось найти способ сделать это. Это может быть не лучшим способом, но это работает. Надеюсь, этот пост может сэкономить кому-то еще много времени и усилий.

В этих инструкциях предполагается, что вы завершили как базовый учебник StockWatcher , так и модификацию StockWatcher Google App Engine .

Создание клиентской реализации класса Stock

Есть несколько вещей, которые нужно помнить о GWT:

  1. Серверные классы могут импортировать клиентские классы, но не наоборот (обычно).
  2. Клиентская сторона не может импортировать какие-либо библиотеки Google App Engine (например, com.google.appengine.api.users.User)

В связи с обоими пунктами выше, клиент никогда не сможет реализовать класс Stock, который мы создали в com.google.gwt.sample.stockwatcher.server. Вместо этого мы создадим новый класс Stock на стороне клиента с именем StockClient.

StockClient.java:

package com.google.gwt.sample.stockwatcher.client;

import java.io.Serializable;
import java.util.Date;

public class StockClient implements Serializable {

  private Long id;
  private String symbol;
  private Date createDate;

  public StockClient() {
    this.createDate = new Date();
  }

  public StockClient(String symbol) {
    this.symbol = symbol;
    this.createDate = new Date();
  }

  public StockClient(Long id, String symbol, Date createDate) {
    this();
    this.id = id;
    this.symbol = symbol;
    this.createDate = createDate;
  }

  public Long getId() {
      return this.id;
  }

  public String getSymbol() {
      return this.symbol;
  }

  public Date getCreateDate() {
      return this.createDate;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public void setSymbol(String symbol) {
      this.symbol = symbol;
  }
}

Изменение клиентских классов для использования StockClient [] вместо String []

Теперь мы сделаем несколько простых изменений в клиентских классах, чтобы они знали, что вызов RPC возвращает StockClient [] вместо String [].

StockService.java:

package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.sample.stockwatcher.client.NotLoggedInException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("stock")
public interface StockService extends RemoteService {
  public Long addStock(String symbol) throws NotLoggedInException;
  public void removeStock(String symbol) throws NotLoggedInException;
  public StockClient[] getStocks() throws NotLoggedInException;
}

StockServiceAsync.java:

package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.sample.stockwatcher.client.StockClient;
import com.google.gwt.user.client.rpc.AsyncCallback;

public interface StockServiceAsync {
  public void addStock(String symbol, AsyncCallback<Long> async);
  public void removeStock(String symbol, AsyncCallback<Void> async);
  public void getStocks(AsyncCallback<StockClient[]> async);
}

StockWatcher.java:

Добавить один импорт:

import com.google.gwt.sample.stockwatcher.client.StockClient;

Все остальные коды остаются прежними, кроме addStock, loadStocks и displayStocks:

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

Измените класс StockServiceImpl, чтобы он возвращал StockClient []

Наконец, мы модифицируем метод getStocks класса StockServiceImpl, чтобы он преобразовывал классы Stock на стороне сервера в классы StockClient на стороне клиента перед возвратом массива.

StockServiceImpl.java

import com.google.gwt.sample.stockwatcher.client.StockClient;

Нам нужно немного изменить метод addStock, чтобы сгенерированный идентификатор был возвращен:

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, но вызывает ошибку в режиме хостинга Google Web Toolkit:

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

Дайте мне знать, если вы столкнулись с той же проблемой или нет. Тот факт, что он работает в Google App Engine, свидетельствует об ошибке в размещенном режиме.

4 голосов
/ 06 июня 2009

GWT требуется файл .java в дополнение к файлу .class. Кроме того, Stock должен находиться в «клиентском» месте модуля GWT.

3 голосов
/ 06 июня 2009

Компилятор GWT не знает о Stock, потому что он находится не в том месте, куда он смотрит. Вы можете либо переместить его в папку клиента, либо, если это имеет больше смысла, оставить его там, где он есть, и создать ModuleName.gwt. .xml, который ссылается на любые другие классы, которые вы хотите, и ваш файл Main.gwt.xml наследуется от него.

Например: DomainGwt.gwt.xml

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

и:

<module rename-to="gwt_ui">
    <inherits name="com.google.gwt.user.User"/>
    <inherits name="au.com.groundhog.groundpics.DomainGwt"/>

    <entry-point class="au.com.groundhog.groundpics.gwt.client.GPicsUIEntryPoint"/>
</module>
1 голос
/ 12 октября 2010

У меня возникла та же проблема, и вывод "mvn gwt: compile" не очень помог. Вместо этого, когда я попытался выполнить развертывание на tomcat (через плагин maven tomcat: mvn tomcat: deploy), я получил полезные сообщения об ошибках.

Несколько вещей, которые мне пришлось исправить:

  1. Сделать объект, отправляемый с клиента на сервер, реализовать Serializable
  2. Добавить конструктор пустого аргумента к тому же объекту
1 голос
/ 21 июля 2010

Здесь есть лучший ответ: GWT Простая проблема варианта использования RPC: включенный код

По сути, вы можете добавить параметры в файл APPNAME.gwt.xml, чтобы компилятор указывал компилятору путь к классу на стороне сервера.

0 голосов
/ 07 февраля 2014

Существует гораздо более простое и легкое решение для этого. Если вы хотите отправить объект вашего пользовательского класса с серверной стороны на клиентскую, вы должны определить этот пользовательский класс в пакете shared .

Например, для вашего случая вам просто нужно перенести класс Stock.java (путем перетаскивания) в

com.google.gwt.sample.stockwatcher.shared

пакет. Однако из скриншота иерархии пакетов кажется, что вы удалили этот общий пакет. Просто заново создайте этот пакет, поместите в него Stock.java и начните игру.

0 голосов
/ 04 февраля 2012

Отказ от ответа Рустишельфа выше ...

В моем случае мне нужно было отредактировать файл ModuleName.gwt.xml и добавить следующее:

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

Я создал свой проект с помощью мастера Создать-> Проект веб-приложения , но не установил флажок Создать пример кода проекта . Затем я создал общий пакет. Если бы я не снял галочку, пакет был бы создан для меня, а файл xml был изменен в соответствии с вышеизложенным.

0 голосов
/ 19 августа 2009

Да, мы уверены, что нам нужно использовать сериализацию для передачи объектов сервера клиенту. Эти модили ?? настройки файла не будут работать для использования класса Stock на стороне клиента.

В вашем случае у вас есть только один класс Stock, и вы можете создать StockClient на стороне клиента. Это легко. Но каким будет решение, если у кого-то будет больше занятий. Нечто подобное свойствам этого класса также относится к другим типам классов.

Пример: stock.getEOD(date).getHigh();

getEOD вернет другой класс с указанной датой, и этот класс имеет метод getHigh.

Что делать в таких больших случаях? Я не думаю, что создание всех классов, реализующих сериализацию на стороне клиента, хорошо для этого. Затем мы должны написать код как на сервере, так и на клиенте. все классы два раза.

...