Подтвердить что ты не робот

Проблемы, связанные с передачей объектов класса через GWT RPC

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

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

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

Хостинг в режиме веб-инструментария Google http://i44.tinypic.com/a47r83.jpg

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

Иерархия пакетов Eclipse http://i43.tinypic.com/14tr5gk.jpg

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

EDIT:. Забыл упомянуть, что класс Stock устойчив, поэтому он должен оставаться на стороне сервера.

4b9b3361

Ответ 1

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

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

Создать клиентскую реализацию класса запаса

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

  • Классы на стороне сервера могут импортировать классы на стороне клиента, но не наоборот (обычно).
  • Клиентская сторона не может импортировать любые библиотеки приложений 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 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, чтобы он переводил классы запаса на стороне сервера в классы клиентской стороны 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, кажется, указывает на ошибку в Hosted Mode.

Ответ 2

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

Ответ 3

Компилятор GWT не знает о запасе, потому что он не находится в том месте, в котором он находится. Вы можете либо переместить его в папку клиента, либо если это имеет смысл оставить его там, где он есть, и создать 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>

Ответ 5

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

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

  • Сделать объект, отправленный с клиента на сервер, реализовать Serializable
  • Добавьте конструктор empty-arg к этому же объекту

Ответ 6

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

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

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

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

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

Ответ 7

Отключение ответа rustyshelf выше...

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

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

Я создал свой проект с помощью мастера New- > Web Application Project, но не снял флажок "Создать образец кода проекта". Затем я создал общий пакет. Если бы я не снял флажок, пакет был бы создан для меня и XML файла, измененного в соответствии с приведенным выше.

Ответ 8

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

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

com.google.gwt.sample.stockwatcher.shared

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