I play with this example of a Google Web / GSON tool that receives data from Twitter.
Everything compiles just fine, but when I debug the web application, everything loads just fine, but when I click the Search button, I get the following error:
SEVERE: javax.servlet.ServletContext log: exception when sending an incoming RPC call com.google.gwt.user.server.rpc.UnexpectedException: service method 'public abstract java.util.List com.google.gwt.twittersearch.client.TwitterService .searchTweets (java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException 'threw an unexpected exception: java.lang.RuntimeException: Failed to call the no-args constructor for class com.google.gwt.twittersearch.server .TwitterServiceImpl $ SearchResponse. Registering InstanceCreator with Gson for this type can solve this problem. at com.google.gwt.user.server.rpc.RPC.encodeResponseForFailure (RPC.javahaps85) at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse (RPC.java∗88) on com.google .gwt.user.server.rpc.RemoteServiceServlet.processCall (RemoteServiceServlet.java:208) on com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost (RemoteServiceServlet.java:248) on com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost (AbstractRemoteServiceServletjavajava.ava .servlet.http.HttpServlet.service (HttpServlet.java:637) in javax.servlet.http.HttpServlet.service (HttpServlet.java:717) at org.mortbay.jetty.servlet.ServletHolder.handle (ServletHolder. ) at org.mortbay.jetty.servlet.ServletHandler $ CachedChain.doFilter (ServletHandler.java:1166) at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter (HeaderVerificationFilter.java:35) at org.mortbay.jet servlet.ServletHandler $ CachedChain.doFilter (ServletHandler.java:1157) at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter (ServeBlobFilter.java:60) at org.mortbay.jetty.servlet.ServletHandler $ CachedChain.doFilter (ServletHandler.java:1157) at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter (TransactionCleanupFilter.java:43) at org.mortbay.jetty.servlet.ServletHanddo $ ServletHandler.java:1157) at com.google.appengine.tools.development.StaticFileFilter.doFilter (StaticFileFilter.java:125) at org.mortbay.jetty.servlet.ServletHandler $ CachedChain.doFilter (ServletHandler) comava11 .google.appengine.tools.development.BackendServersFilter.doFilter (BackendServersFilter.java:97) at org.mortbay.jetty.servlet.ServletHandler $ CachedChain.doFilter (ServletHandler.java:1157) at org.mortbay.jlettyervletandjervtyervlet.jlettyervlet .handle (ServletHandler.javahaps88) at org.mortbay.jetty.security.SecurityHandler.handle (SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle (SessionHandler.java:182) at org.mortbay.jetty.handler.ContextHandler.handle (ContextHandler.java:765) in org.mortbay.jetty.webapp.WebAppContext.handle (WebAppContext.java:418) at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle (DevAppEngineWebAppContext.java:94) at org.mortbay.jetty.handler.HandlerWrapper.handle (HandlerWrapper.java:152) on com.google.dement .JettyContainerService $ ApiProxyHandler.handle (JettyContainerService.javahaps70) at org.mortbay.jetty.handler.HandlerWrapper.handle (HandlerWrapper.java:152) at org.mortbay.jetty.Server.handle (Server.java:6 org.mortbay.jetty.HttpConnection.handleRequest (HttpConnection.java∗42) at org.mortbay.jetty.HttpConnection $ RequestHandler.content (HttpConnection.java:938) at org.mortbay.jetty.HttpParser.parseNext (HttpParser.java:755) at org.mortbay.jetty.HttpParser.parseAvailable (HttpParser.java:218) at org.mortbay.jetty.HttpConnection.handle (HttpConnection.java404 .nio.SelectChannelEndPoint.run (SelectChannelEndPoint.java:409) at org.mortbay.thread.QueuedThreadPool $ PoolThread.run (QueuedThreadPool.java∗82) Raised: java.lang.RuntimeException: Cannot call the no-args constructor for class com. google.gwt.twittersearch.server.TwitterServiceImpl $ SearchResponse. Registering InstanceCreator with Gson for this type can solve this problem. at com.google.gson.internal.ConstructorConstructor $ 8.construct (ConstructorConstructor.java:167) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.read (ReflectiveTypeAdapterFactory.java:162) in com.google.gson. Gson.fromJson (Gson.java: 795) on com.google.gson.Gson.fromJson (Gson.java:734) on com.google.gwt.twittersearch.server.TwitterServiceImpl.parseSearchResponse (TwitterServiceImpl.java:80) on com.google.gwt.twittersearch .server.TwitterServiceImpl.searchTweets (TwitterServiceImpl.java:35) at sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.javalectMethodativePloteMethreativeMethreleccessPlote.LetMeodreLessfReLetf.AccessMelodfreatLaccessMeTod .java: 25) in java.lang.reflect.Method.invoke (Method.java∗97) in com.google.appengine.tools.development.agent.runtime.Runtime.invoke (Runtime.java:115) on com. google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse (RPC.java//69) ... 34 more. Called: java.lang.UnsupportedOperationException: class com.google.gwt cannot be allocated.twittersearch.server.TwitterServiceImpl $ SearchResponse at com.google.gson.internal.UnsafeAllocator $ 4.newInstance (UnsafeAllocator.java:100) at com.google.gson.internal.ConstructorConstructor $ 8.construct (ConstructorConstructor.java:164) 45 more
TwitterServiceImpl:
package com.google.gwt.twittersearch.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;
import com.google.gwt.twittersearch.client.Tweet;
import com.google.gwt.twittersearch.client.TwitterService;
import com.google.gson.Gson;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
@SuppressWarnings("serial")
public class TwitterServiceImpl extends RemoteServiceServlet implements
TwitterService {
@Override
public List<Tweet> searchTweets(String query) throws IllegalArgumentException, IOException {
query = query.trim();
if (query.isEmpty()) {
throw new IllegalArgumentException("No search query specified.");
}
String q = URLEncoder.encode(query, "UTF-8");
URL url = new URL("http://search.twitter.com/search.json?q=" + q);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream response = null;
try {
response = connection.getInputStream();
return parseSearchResponse(response);
} finally {
if (response != null) {
response.close();
}
}
}
@Override
public String getPrivacyPolicy() throws IOException {
URL url = new URL("https://api.twitter.com/1/legal/privacy.json");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream response = null;
try {
response = connection.getInputStream();
return parsePolicyResponse(response);
} finally {
if (response != null) {
response.close();
}
}
}
private String parsePolicyResponse(InputStream response) throws IOException {
Reader reader = new InputStreamReader(response);
PrivacyPolicyResponse privacyPolicyResponse = new Gson().fromJson(reader, PrivacyPolicyResponse.class);
return privacyPolicyResponse.privacy;
}
private List<Tweet> parseSearchResponse(InputStream response) throws IOException {
Reader reader = new InputStreamReader(response);
SearchResponse searchResponse = new Gson().fromJson(reader, SearchResponse.class);
return searchResponse.results;
}
private class PrivacyPolicyResponse {
public String privacy;
}
private class SearchResponse {
public List<Tweet> results;
}
}
:
package com.google.gwt.twittersearch.client;
import java.util.List;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
public class TwitterSearch implements EntryPoint {
private Button privacyPolicyButton;
private Button searchButton;
private TextBox searchQueryTextBox;
private Panel resultsPanel;
private Label errorLabel;
private Image loadingImage;
private final TwitterServiceAsync service = GWT.create(TwitterService.class);
public void onModuleLoad() {
createWidgets();
layoutWidgets();
}
private void createWidgets() {
searchQueryTextBox = new TextBox();
searchButton = new Button("Search");
searchButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
setLoading(true);
String query = searchQueryTextBox.getText();
service.searchTweets(query, new AsyncCallback<List<Tweet>>() {
@Override
public void onFailure(Throwable caught) {
errorLabel.setText(caught.getMessage());
errorLabel.setVisible(true);
setLoading(false);
}
@Override
public void onSuccess(List<Tweet> result) {
resultsPanel.clear();
for (Tweet tweet : result) {
SafeHtmlBuilder builder = new SafeHtmlBuilder();
builder.appendHtmlConstant("<b>User: </b>");
builder.appendEscaped(tweet.getFrom_user());
builder.appendHtmlConstant("<br /><b>Created: </b>");
builder.appendEscaped(tweet.getCreated_at());
builder.appendHtmlConstant("<br /><b>Tweet: </b>");
builder.appendEscaped(tweet.getText());
builder.appendHtmlConstant("<br /><br />");
resultsPanel.add(new HTML(builder.toSafeHtml()));
}
setLoading(false);
}
});
}
});
privacyPolicyButton = new Button("Privacy Policy");
privacyPolicyButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
setLoading(true);
service.getPrivacyPolicy(new AsyncCallback<String>() {
@Override
public void onFailure(Throwable caught) {
errorLabel.setText(caught.getMessage());
errorLabel.setVisible(true);
setLoading(false);
}
@Override
public void onSuccess(String result) {
resultsPanel.clear();
SafeHtmlBuilder builder = new SafeHtmlBuilder();
builder.appendEscapedLines(result);
resultsPanel.add(new HTML(builder.toSafeHtml()));
setLoading(false);
}
});
}
});
resultsPanel = new VerticalPanel();
errorLabel = new Label();
errorLabel.addStyleName("errorLabel");
errorLabel.setVisible(false);
loadingImage = new Image("loading.gif");
loadingImage.setVisible(false);
}
private void layoutWidgets() {
Panel panel = new VerticalPanel();
panel.add(errorLabel);
Panel horizPanel = new HorizontalPanel();
horizPanel.add(searchQueryTextBox);
horizPanel.add(searchButton);
horizPanel.add(privacyPolicyButton);
horizPanel.add(loadingImage);
panel.add(horizPanel);
panel.add(resultsPanel);
RootPanel.get().add(panel);
}
private void setLoading(boolean loading) {
if (loading) {
errorLabel.setVisible(false);
}
searchQueryTextBox.setEnabled(!loading);
searchButton.setEnabled(!loading);
privacyPolicyButton.setEnabled(!loading);
loadingImage.setVisible(loading);
}
}
:
package com.google.gwt.twittersearch.client;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Tweet implements Serializable{
private String id;
private String from_user;
private String created_at;
private String text;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFrom_user() {
return from_user;
}
public void setFrom_user(String from_user) {
this.from_user = from_user;
}
public String getCreated_at() {
return created_at;
}
public void setCreated_at(String created_at) {
this.created_at = created_at;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
, , !
, brl