import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath(“stockPrices”) public interface StockPriceService extends RemoteService {

StockPrice[] getPrices(String[] symbols); } Implementation Note: Notice the @RemoteServiceRelativePath annotation. This associates the service with a default path relative to the module base URL.

<li>
    <div class="header">Press <code>Finish</code></div>
    <div class="details">Eclipse creates the package<br />
    <code>com.google.gwt.sample.stockwatcher.server</code></div>
     <div class="details">Eclipse creates a stub StockPriceServiceImpl class.</div>
   <div class="details"><pre class="code">

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

import com.google.gwt.sample.stockwatcher.client.StockPrice; import com.google.gwt.sample.stockwatcher.client.StockPriceService; import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class StockPriceServiceImpl extends RemoteServiceServlet implements StockPriceService {

public StockPrice[] getPrices(String[] symbols) { // TODO Auto-generated method stub return null; }

}

StockPrice[] prices = new StockPrice[symbols.length];
for (int i=0; i&lt;symbols.length; i++) {
  double price = rnd.nextDouble() * MAX_PRICE;
  double change = price * MAX_PRICE_CHANGE * (rnd.nextDouble() * 2f - 1f);

  prices[i] = new StockPrice(symbols[i], price, change);
}</span>

return <span class="highlight">prices</span>;

} Eclipse flags Random and suggests you include the import declaration. Include the import declaration from java.util, not from com.google.gwt.user.client. import java.util.Random; Implementation Note: Remember the service implementation runs on the server as Java bytecode, so you can use any Java class or library without worrying about whether it's translatable to JavaScript. In this case, you can use the Random class from the Java runtime library (java.util.Random) instead of the emulated GWT version (com.google.gwt.user.client.Random). This listing shows the completed StockPriceServiceImpl class. package com.google.gwt.sample.stockwatcher.server;

import com.google.gwt.sample.stockwatcher.client.StockPrice; import com.google.gwt.sample.stockwatcher.client.StockPriceService; import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import java.util.Random;

public class StockPriceServiceImpl extends RemoteServiceServlet implements StockPriceService {

private static final double MAX_PRICE = 100.0; // $100.00 private static final double MAX_PRICE_CHANGE = 0.02; // +/- 2%

public StockPrice[] getPrices(String[] symbols) { Random rnd = new Random();

StockPrice[] prices = new StockPrice[symbols.length];
for (int i=0; i&lt;symbols.length; i++) {
  double price = rnd.nextDouble() * MAX_PRICE;
  double change = price * MAX_PRICE_CHANGE * (rnd.nextDouble() * 2f - 1f);

  prices[i] = new StockPrice(symbols[i], price, change);
}

return prices;

}

}

Because you've mapped the StockPriceService to “stockPrices” and the module rename-to attribute in StockWatcher.gwt.xml is “stockwatcher”, the full URL will be:
http://localhost:8888/stockwatcher/stockPrices

<web-app>

<!-- Default page to serve --> <welcome-file-list> <welcome-file>StockWatcher.html</welcome-file> </welcome-file-list>

<!-- Servlets --> <servlet> <servlet-name>stockPriceServiceImpl</servlet-name> <servlet-class>com.google.gwt.sample.stockwatcher.server.StockPriceServiceImpl</servlet-class> </servlet>

<servlet-mapping> <servlet-name>stockPriceServiceImpl</servlet-name> <url-pattern>/stockwatcher/stockPrices</url-pattern> </servlet-mapping>

</web-app>

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface StockPriceServiceAsync {

void getPrices(String[] symbols, AsyncCallback<StockPrice[]> callback);

}

You specify the callback method by passing an AsyncCallback object to the service proxy class.

The AsyncCallback object contains two methods, one of which is called depending on whether the call failed or succeeded: onFailure(Throwable) and onSuccess(T).

// Set up the callback object.
AsyncCallback&lt;StockPrice[]&gt; callback = new AsyncCallback&lt;StockPrice[]&gt;() {
  public void onFailure(Throwable caught) {
    // TODO: Do something with errors.
  }

  public void onSuccess(StockPrice[] result) {
    updateTable(result);
  }
};

// Make the call to the stock price service.
stockPriceSvc.getPrices(stocks.toArray(new String[0]), callback);</span>

} Eclipse flags AsyncCallback. Include the import declarations. import com.google.gwt.core.client.GWT;

import com.google.gwt.user.client.rpc.AsyncCallback;

For more information on what is and is not serializable in GWT, see the Developer's Guide, Serializable Types.

import java.io.Serializable;

public class StockPrice implements Serializable {

private String symbol; private double price; private double change;

...

<li>
    <div class="header">Replace the stub with the following code.</div>
    <div class="details"><pre class="code">

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

import java.io.Serializable;

public class DelistedException extends Exception implements Serializable {

private String symbol;

public DelistedException() { }

public DelistedException(String symbol) { this.symbol = symbol; }

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

import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath(“stockPrices”) public interface StockPriceService extends RemoteService {

StockPrice[] getPrices(String[] symbols) throws DelistedException; }

...

public StockPrice[] getPrices(String[] symbols) throws DelistedException { Random rnd = new Random();

StockPrice[] prices = new StockPrice[symbols.length];
for (int i=0; i&lt;symbols.length; i++) {

if (symbols[i].equals(“ERR”)) { throw new DelistedException(“ERR”); }

  double price = rnd.nextDouble() * MAX_PRICE;
  double change = price * MAX_PRICE_CHANGE * (rnd.nextDouble() * 2f - 1f);

  prices[i] = new StockPrice(symbols[i], price, change);
}

return prices;

}

.errorMessage { color: red; } To hold the text of the error message, add a Label widget. In StockWatcher.java, add the following instance field. private StockPriceServiceAsync stockPriceSvc = GWT.create(StockPriceService.class); private Label errorMsgLabel = new Label(); Initialize the errorMsgLabel when StockWatcher launches. In the onModuleLoad method, add a secondary class attribute to errorMsgLabel and do not display it when StockWatcher loads. Add the error message to the Main panel above the stocksFlexTable.

    <div class="details"><pre class="code">
// Assemble Add Stock panel.
addPanel.add(newSymbolTextBox);
addPanel.add(addButton);
addPanel.addStyleName("addPanel");

// Assemble Main panel.

errorMsgLabel.setStyleName(“errorMessage”); errorMsgLabel.setVisible(false);

mainPanel.add(errorMsgLabel);</span>
mainPanel.add(stocksFlexTable);
mainPanel.add(addPanel);
mainPanel.add(lastUpdatedLabel);

    errorMsgLabel.setText("Error: " + details);
    errorMsgLabel.setVisible(true);</span>
  }</pre></div>
</li>
<li>
    <div class="header">If the error is corrected, hide the error message widget.</div>
    <div class="details">In the updateTable(StockPrice[] prices) method, clear any errors.</div>
    <div class="details"><pre class="code">

private void updateTable(StockPrice[] prices) { for (int i=0; i < prices.length; i++) { updateTable(prices[i]); }

// Display timestamp showing last refresh.
lastUpdatedLabel.setText("Last update : " +
    DateTimeFormat.getMediumDateTimeFormat().format(new Date()));

// Clear any errors. errorMsgLabel.setVisible(false); }

</li>
<li>
    <div class="header">Delete the stock code, ERR.</div>
</li>
<li>
    <div class="header">The error message is removed. Valid stock code data resumes being refreshed.</div>
</li>