Inital commit

This commit is contained in:
Tilman
2011-10-26 09:44:02 +02:00
commit 5971f4b417
1813 changed files with 737837 additions and 0 deletions
@@ -0,0 +1,227 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.basic;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.StringTokenizer;
/**
* Add a new item to Google Base using the Google Base data API server.
*/
public class InsertExample {
/**
* URL of the authenticated customer feed.
*/
private static final String ITEMS_FEED = "http://base.google.com/base/feeds/items";
/**
* The data item we are going to insert, in XML/Atom format.
*/
private static final String DATA_ITEM =
"<?xml version='1.0'?>\n" +
"<entry xmlns='http://www.w3.org/2005/Atom'\n" +
" xmlns:g='http://base.google.com/ns/1.0'>\n" +
" <g:item_type type='text'>testrecipes</g:item_type>\n" +
" <title type='text'>Fabulous cheese cake</title>\n" +
" <content type='xhtml'>All that you need is lots of patience.</content>\n" +
"</entry>";
/**
* URL used for authenticating and obtaining an authentication token.
* More details about how it works:
* <code>http://code.google.com/apis/accounts/AuthForInstalledApps.html<code>
*/
private static final String AUTHENTICATION_URL = "https://www.google.com/accounts/ClientLogin";
/**
* Fill in your Google Account email here.
*/
private static final String EMAIL = "";
/**
* Fill in your Google Account password here.
*/
private static final String PASSWORD = "";
/**
* The main method constructs a <code>InsertExample</code> instance, obtains an
* authorization token and posts a new item to Google Base.
*/
public static void main(String[] args) throws MalformedURLException, IOException {
InsertExample insertExample = new InsertExample();
String token = insertExample.authenticate();
System.out.println("Obtained authorization token: " + token);
insertExample.postItem(token);
}
/**
* Inserts <code>DATA_ITEM</code> by making a POST request to
* <code>ITEMS_URL<code>.
* @param token authentication token obtained using <code>authenticate</code>
* @throws IOException if an I/O exception occurs while creating/writing/
* reading the request
*/
public void postItem(String token) throws IOException {
HttpURLConnection connection = (HttpURLConnection)(new URL(ITEMS_FEED)).openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
// Set the properties of the connection: the Http method, the content type
// of the POST request and the authorization header
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/atom+xml");
connection.setRequestProperty("Authorization", "GoogleLogin auth=" + token);
// Post the data item
OutputStream outputStream = connection.getOutputStream();
outputStream.write(DATA_ITEM.getBytes());
outputStream.close();
// Retrieve the output
int responseCode = connection.getResponseCode();
InputStream inputStream;
if (responseCode == HttpURLConnection.HTTP_CREATED) {
inputStream = connection.getInputStream();
} else {
inputStream = connection.getErrorStream();
}
// write the output to the console
System.out.println(toString(inputStream));
}
/**
* Retrieves the authentication token for the provided set of credentials.
* @return the authorization token that can be used to access authenticated
* Google Base data API feeds
*/
public String authenticate() {
// create the login request
String postOutput = null;
try {
URL url = new URL(AUTHENTICATION_URL);
postOutput = makeLoginRequest(url);
} catch (IOException e) {
System.out.println("Could not connect to authentication server: "
+ e.toString());
System.exit(1);
}
// Parse the result of the login request. If everything went fine, the
// response will look like
// HTTP/1.0 200 OK
// Server: GFE/1.3
// Content-Type: text/plain
// SID=DQAAAGgA...7Zg8CTN
// LSID=DQAAAGsA...lk8BBbG
// Auth=DQAAAGgA...dk3fA5N
// so all we need to do is look for "Auth" and get the token that comes after it
StringTokenizer tokenizer = new StringTokenizer(postOutput, "=\n ");
String token = null;
while (tokenizer.hasMoreElements()) {
if (tokenizer.nextToken().equals("Auth")) {
if (tokenizer.hasMoreElements()) {
token = tokenizer.nextToken();
}
break;
}
}
if (token == null) {
System.out.println("Authentication error. Response from server:\n" + postOutput);
System.exit(1);
}
return token;
}
/**
* Makes a HTTP POST request to the provided {@code url} given the provided
* {@code parameters}. It returns the output from the POST handler as a
* String object.
*
* @param url the URL to post the request
* @return the output from the handler
* @throws IOException if an I/O exception occurs while
* creating/writing/reading the request
*/
private String makeLoginRequest(URL url)
throws IOException {
// Create a login request. A login request is a POST request that looks like
// POST /accounts/ClientLogin HTTP/1.0
// Content-type: application/x-www-form-urlencoded
// Email=johndoe@gmail.com&Passwd=north23AZ&service=gbase&source=Insert Example
// Open connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// Set properties of the connection
urlConnection.setRequestMethod("POST");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
// Form the POST parameters
StringBuilder content = new StringBuilder();
content.append("Email=").append(URLEncoder.encode(EMAIL, "UTF-8"));
content.append("&Passwd=").append(URLEncoder.encode(PASSWORD, "UTF-8"));
content.append("&source=").append(URLEncoder.encode("Google Base data API example", "UTF-8"));
content.append("&service=").append(URLEncoder.encode("gbase", "UTF-8"));
OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(content.toString().getBytes("UTF-8"));
outputStream.close();
// Retrieve the output
int responseCode = urlConnection.getResponseCode();
InputStream inputStream;
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = urlConnection.getInputStream();
} else {
inputStream = urlConnection.getErrorStream();
}
return toString(inputStream);
}
/**
* Writes the content of the input stream to a <code>String<code>.
*/
private String toString(InputStream inputStream) throws IOException {
String string;
StringBuilder outputBuilder = new StringBuilder();
if (inputStream != null) {
BufferedReader reader =
new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while (null != (string = reader.readLine())) {
outputBuilder.append(string).append('\n');
}
}
return outputBuilder.toString();
}
}
@@ -0,0 +1,63 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.basic;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
/**
* Dumps to the console the response of a Google Base data API query.
*/
public class QueryExample1 {
/**
* Url of the Google Base data API snippet feed.
*/
private static final String SNIPPETS_FEED = "http://base.google.com/base/feeds/snippets";
/**
* The query that is sent over to the Google Base data API server.
*/
private static final String QUERY = "cars [item type : products]";
/**
* Connect to <code>SNIPPETS_FEED</code> and display the response.
* @throws IOException if an error occured while connecting or reading from
* the feed
*/
public void displayItems() throws IOException {
URL url = new URL(SNIPPETS_FEED + "?bq=" +
URLEncoder.encode(QUERY, "UTF-8"));
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
InputStream inputStream = httpConnection.getInputStream();
int ch;
while ((ch=inputStream.read()) > 0) {
System.out.print((char)ch);
}
}
/**
* The main method simply creates a <code>QueryExample2</code> instance and
* calls <code>displayItems</code>.
*/
public static void main(String[] args) throws IOException {
new QueryExample1().displayItems();
}
}
@@ -0,0 +1,148 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.basic;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Stack;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
/**
* Display the titles of the Google Base items that match a specific query.
*/
public class QueryExample2 {
/**
* Url of the Google Base data API snippet feed.
*/
private static final String SNIPPETS_FEED = "http://base.google.com/base/feeds/snippets";
/**
* The query that is sent over to the Google Base data API server.
*/
private static final String QUERY = "cars [item type : products]";
/**
* Create a <code>QueryExample2</code> instance and
* call <code>displayItems</code>.
*/
public static void main(String[] args) throws IOException, SAXException,
ParserConfigurationException {
new QueryExample2().displayItems();
}
/**
* Connect to the Google Base data API server, retrieve the items that match
* <code>QUERY</code> and call <code>DisplayTitlesHandler</code> to extract
* and display the titles from the XML response.
*/
public void displayItems() throws IOException, SAXException,
ParserConfigurationException {
/*
* Create a URL object, open an Http connection on it and get the input
* stream that reads the Http response.
*/
URL url = new URL(SNIPPETS_FEED + "?bq=" +
URLEncoder.encode(QUERY, "UTF-8"));
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = httpConnection.getInputStream();
/*
* Create a SAX XML parser and pass in the input stream to the parser.
* The parser will use DisplayTitleHandler to extract the titles from the
* XML stream.
*/
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(inputStream, new DisplayTitlesHandler());
}
/**
* Simple SAX event handler, which prints out the titles of all entries in the
* Atom response feed.
*/
private static class DisplayTitlesHandler extends DefaultHandler {
/**
* Stack containing the opening XML tags of the response.
*/
private Stack<String> xmlTags = new Stack<String>();
/**
* Counter that keeps track of the currently parsed item.
*/
private int itemNo = 0;
/**
* True if we are inside of a data entry's title, false otherwise.
*/
private boolean insideEntryTitle = false;
/**
* Receive notification of an opening XML tag: push the tag to
* <code>xmlTags</code>. If the tag is a title tag inside an entry tag,
* turn <code>insideEntryTitle</code> to <code>true</code>.
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (qName.equals("title") && xmlTags.peek().equals("entry")) {
insideEntryTitle = true;
System.out.print("Item " + ++itemNo + ": ");
}
xmlTags.push(qName);
}
/**
* Receive notification of a closing XML tag: remove the tag from teh stack.
* If we were inside of an entry's title, turn <code>insideEntryTitle</code>
* to <code>false</code>.
*/
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// If a "title" element is closed, we start a new line, to prepare
// printing the new title.
xmlTags.pop();
if (insideEntryTitle) {
insideEntryTitle = false;
System.out.println();
}
}
/**
* Callback method for receiving notification of character data inside an
* XML element.
*/
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// display the character data if the opening tag is "title" and its parent is
// "entry"
if (insideEntryTitle) {
System.out.print(new String(ch, start, length));
}
}
}
}
@@ -0,0 +1,204 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.basic;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.StringTokenizer;
/**
* Display all items of a specific customer.
*/
public class QueryExample3 {
/**
* URL of the authenticated customer feed.
*/
private static final String ITEMS_FEED = "http://base.google.com/base/feeds/items";
/**
* URL used for authenticating and obtaining an authentication token.
* More details about how it works:
* <code>http://code.google.com/apis/accounts/AuthForInstalledApps.html<code>
*/
private static final String AUTHENTICATION_URL = "https://www.google.com/accounts/ClientLogin";
/**
* Fill in your Google Account email here.
*/
private static final String EMAIL = "";
/**
* Fill in your Google Account password here.
*/
private static final String PASSWORD = "";
/**
* Create a <code>QueryExample3</code> instance and call
* <code>displayMyItems</code>, which displays all items that belong to the
* currently authenticated user.
*/
public static void main(String[] args) throws IOException {
QueryExample3 queryExample = new QueryExample3();
String token = queryExample.authenticate();
queryExample.displayMyItems(token);
}
/**
* Retrieves the authentication token for the provided set of credentials.
* @return the authorization token that can be used to access authenticated
* Google Base data API feeds
*/
public String authenticate() {
// create the login request
String postOutput = null;
try {
URL url = new URL(AUTHENTICATION_URL);
postOutput = makeLoginRequest(url);
} catch (IOException e) {
System.out.println("Could not connect to authentication server: "
+ e.toString());
System.exit(1);
}
// Parse the result of the login request. If everything went fine, the
// response will look like
// HTTP/1.0 200 OK
// Server: GFE/1.3
// Content-Type: text/plain
// SID=DQAAAGgA...7Zg8CTN
// LSID=DQAAAGsA...lk8BBbG
// Auth=DQAAAGgA...dk3fA5N
// so all we need to do is look for "Auth" and get the token that comes after it
StringTokenizer tokenizer = new StringTokenizer(postOutput, "=\n ");
String token = null;
while (tokenizer.hasMoreElements()) {
if (tokenizer.nextToken().equals("Auth")) {
if (tokenizer.hasMoreElements()) {
token = tokenizer.nextToken();
}
break;
}
}
if (token == null) {
System.out.println("Authentication error. Response from server:\n" + postOutput);
System.exit(1);
}
return token;
}
/**
* Displays the "items" feed, that is the feed that contains the items that
* belong to the currently authenticated user.
*
* @param token the authorization token, as returned by
* <code>authenticate<code>
* @throws IOException if an IOException occurs while creating/reading the
* request
*/
public void displayMyItems(String token) throws MalformedURLException, IOException {
HttpURLConnection connection = (HttpURLConnection)(new URL(ITEMS_FEED)).openConnection() ;
// Set properties of the connection
connection.setRequestMethod("GET");
connection.setRequestProperty("Authorization", "GoogleLogin auth=" + token);
int responseCode = connection.getResponseCode();
InputStream inputStream;
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = connection.getInputStream();
} else {
inputStream = connection.getErrorStream();
}
System.out.println(toString(inputStream));
}
/**
* Makes a HTTP POST request to the provided {@code url} given the provided
* {@code parameters}. It returns the output from the POST handler as a
* String object.
*
* @param url the URL to post the request
* @return the output from the Google Accounts server, as string
* @throws IOException if an I/O exception occurs while
* creating/writing/reading the request
*/
private String makeLoginRequest(URL url)
throws IOException {
// Create a login request. A login request is a POST request that looks like
// POST /accounts/ClientLogin HTTP/1.0
// Content-type: application/x-www-form-urlencoded
// Email=johndoe@gmail.com&Passwd=north23AZ&service=gbase&source=Insert Example
// Open connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// Set properties of the connection
urlConnection.setRequestMethod("POST");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
// Form the POST parameters
StringBuilder content = new StringBuilder();
content.append("Email=").append(URLEncoder.encode(EMAIL, "UTF-8"));
content.append("&Passwd=").append(URLEncoder.encode(PASSWORD, "UTF-8"));
content.append("&service=").append(URLEncoder.encode("gbase", "UTF-8"));
content.append("&source=").append(URLEncoder.encode("Google Base data API example", "UTF-8"));
OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(content.toString().getBytes("UTF-8"));
outputStream.close();
// Retrieve the output
int responseCode = urlConnection.getResponseCode();
InputStream inputStream;
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = urlConnection.getInputStream();
} else {
inputStream = urlConnection.getErrorStream();
}
return toString(inputStream);
}
/**
* Writes the content of the input stream to a <code>String<code>.
*/
private String toString(InputStream inputStream) throws IOException {
String string;
StringBuilder outputBuilder = new StringBuilder();
if (inputStream != null) {
BufferedReader reader =
new BufferedReader(new InputStreamReader(inputStream));
while (null != (string = reader.readLine())) {
outputBuilder.append(string).append('\n');
}
}
return outputBuilder.toString();
}
}
@@ -0,0 +1,305 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.basic;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.StringTokenizer;
/**
* Update a Google Base data item using the Google Base data API server.
*/
public class UpdateExample {
/**
* URL of the authenticated customer feed.
*/
private static final String ITEMS_FEED = "http://base.google.com/base/feeds/items";
/**
* The data item we are going to insert, in XML/Atom format.
*/
private static final String DATA_ITEM =
"<?xml version='1.0'?>\n" +
"<entry xmlns='http://www.w3.org/2005/Atom'\n" +
" xmlns:g='http://base.google.com/ns/1.0'>\n" +
" <g:item_type type='text'>testrecipes</g:item_type>\n" +
" <title type='text'>Fabulous cheese cake</title>\n" +
" <content type='xhtml'>All that you need is lots of patience.</content>\n" +
"</entry>";
/**
* The updated data item, in XML/Atom format. It adds an additional label to
* <code>DATA_ITEM</code>.
*/
private static final String NEW_DATA_ITEM =
"<?xml version='1.0'?>\n" +
"<entry xmlns='http://www.w3.org/2005/Atom'\n" +
" xmlns:g='http://base.google.com/ns/1.0'>\n" +
" <g:item_type type='text'>testrecipes</g:item_type>\n" +
" <title type='text'>Yummy cheese cake</title>\n" +
" <content type='xhtml'>All that you need is lots of patience.</content>\n" +
" <g:main_ingredient type='text'>cheese</g:main_ingredient>\n" +
"</entry>";
/**
* URL used for authenticating and obtaining an authentication token.
* More details about how it works:
* <code>http://code.google.com/apis/accounts/AuthForInstalledApps.html<code>
*/
private static final String AUTHENTICATION_URL =
"https://www.google.com/accounts/ClientLogin";
/**
* Fill in your Google Account email here.
*/
private static final String EMAIL = "";
/**
* Fill in your Google Account password here.
*/
private static final String PASSWORD = "";
/**
* The main method constructs an <code>UpdateExample</code> instance,
* obtains an authorization token, posts a new item to Google Base, extracts
* the new item's id from the reponse and uses it to update the item.
*/
public static void main(String[] args)
throws MalformedURLException, IOException {
UpdateExample updateExample = new UpdateExample();
String token = updateExample.authenticate();
System.out.println("Obtained authorization token: " + token);
System.out.println("Posting item:\n" + DATA_ITEM);
String itemUrl = updateExample.extractItemUrlFromResponse(
updateExample.postItem(token));
System.out.println("Updating item: " + itemUrl);
String updateResponse = updateExample.updateItem(token, itemUrl);
System.out.println(updateResponse);
}
/**
* Retrieves the authentication token for the provided set of credentials.
* @return the authorization token that can be used to access authenticated
* Google Base data API feeds
*/
public String authenticate() {
// create the login request
String postOutput = null;
try {
URL url = new URL(AUTHENTICATION_URL);
postOutput = makeLoginRequest(url);
} catch (IOException e) {
System.out.println("Could not connect to authentication server: "
+ e.toString());
System.exit(1);
}
// Parse the result of the login request. If everything went fine, the
// response will look like
// HTTP/1.0 200 OK
// Server: GFE/1.3
// Content-Type: text/plain
// SID=DQAAAGgA...7Zg8CTN
// LSID=DQAAAGsA...lk8BBbG
// Auth=DQAAAGgA...dk3fA5N
// so all we need to do is look for "Auth" and get the token that comes after it
StringTokenizer tokenizer = new StringTokenizer(postOutput, "=\n ");
String token = null;
while (tokenizer.hasMoreElements()) {
if (tokenizer.nextToken().equals("Auth")) {
if (tokenizer.hasMoreElements()) {
token = tokenizer.nextToken();
}
break;
}
}
if (token == null) {
System.out.println("Authentication error. Response from server:\n" + postOutput);
System.exit(1);
}
return token;
}
/**
* Parse the POST XML response returned by <code>insertResponse</code> and
* extract the Google Base item id of the newly created item. To keep parsing
* simple, we assume that the first "id" tag contains the Atom item id; this
* is true for the Google Base data API servers, but it's not enforced by Atom -
* see how the title is parsed in {@link QueryExample2}
* @param insertResponse the response sent by the Google Base data API server
* after the insert operation
* @return the Url that identifies the item, for example: <code>
* http://base.google.com/base/feeds/items/18020038538902937385</code>
*/
public String extractItemUrlFromResponse(String insertResponse) {
int startIndex = insertResponse.indexOf("<id>") + 4;
int endIndex = insertResponse.indexOf("</id>");
String itemUrl = insertResponse.substring(startIndex, endIndex);
return itemUrl;
}
/**
* Inserts <code>DATA_ITEM</code> by making a POST request to
* <code>ITEMS_FEED<code>.
* @param token authentication token obtained using <code>authenticate</code>
* @return the Google Base data API server's insert response
* @throws IOException if an I/O exception occurs while creating/writing/
* reading the request
*/
public String postItem(String token) throws IOException {
return makeHttpRequest(token, ITEMS_FEED, DATA_ITEM, "POST",
HttpURLConnection.HTTP_CREATED);
}
/**
* Updates <code>NEW_DATA_ITEM</code> by making a PUT request to
* <code>itemUrl</code>.
*
* @param token authentication token obtained using <code>authenticate</code>
* @param itemUrl the identifier of the item to update, which has the form
* <code>ITEMS_URL + "/itemId"</code>
* @return the Google Base data API server's update response
* @throws IOException if an I/O exception occurs while creating/writing/
* reading the request
*/
public String updateItem(String token, String itemUrl)
throws MalformedURLException, IOException {
return makeHttpRequest(token, itemUrl, NEW_DATA_ITEM, "PUT",
HttpURLConnection.HTTP_OK);
}
/**
* Make an Http request to <code>url</code>, using <code>httpMethod</code>,
* post <code>item</code> and return the response.
*
* @param token authentication token obtained using <code>authenticate</code>
* @param url the identifier of the item to update, which has the form
* <code>ITEMS_URL + "/itemId"</code>
* @param item the item to be posted via the request
* @param httpMethod the httpMethod to use for posting (should be PUT or POST)
* @param expectedResponseCode the response code returned in case of a
* successful operation
* @return the Google Base data API update response
* @throws IOException if an I/O exception occurs while
* creating/writing/reading the request
*/
private String makeHttpRequest(String token, String url, String item,
String httpMethod, int expectedResponseCode) throws IOException {
HttpURLConnection connection = (HttpURLConnection)(new URL(url)).openConnection() ;
connection.setDoInput(true);
connection.setDoOutput(true);
// Set the properties of the connection: the http request method, the
// content type and the authorization header
connection.setRequestMethod(httpMethod);
connection.setRequestProperty("Content-Type", "application/atom+xml");
connection.setRequestProperty("Authorization", "GoogleLogin auth=" + token);
// Post the data item
OutputStream outputStream = connection.getOutputStream();
outputStream.write(item.getBytes());
outputStream.close();
// Retrieve the output
int responseCode = connection.getResponseCode();
if (responseCode == expectedResponseCode) {
return toString(connection.getInputStream());
} else {
throw new RuntimeException(toString(connection.getErrorStream()));
}
}
/**
* Makes a HTTP POST request to the provided {@code url} given the provided
* {@code parameters}. It returns the output from the POST handler as a
* String object.
*
* @param url the URL to post the request
* @return the output from the server
* @throws IOException if an I/O exception occurs while
* creating/writing/reading the request
*/
private String makeLoginRequest(URL url)
throws IOException {
// Create a login request. A login request is a POST request that looks like
// POST /accounts/ClientLogin HTTP/1.0
// Content-type: application/x-www-form-urlencoded
// Email=johndoe@gmail.com&Passwd=north23AZ&service=gbase&source=Insert Example
// Open connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// Set properties of the connection
urlConnection.setRequestMethod("POST");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
// Form the POST parameters
StringBuilder content = new StringBuilder();
content.append("Email=").append(URLEncoder.encode(EMAIL, "UTF-8"));
content.append("&Passwd=").append(URLEncoder.encode(PASSWORD, "UTF-8"));
content.append("&source=").append(URLEncoder.encode("Google Base data API example", "UTF-8"));
content.append("&service=").append(URLEncoder.encode("gbase", "UTF-8"));
OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(content.toString().getBytes("UTF-8"));
outputStream.close();
// Retrieve the output
int responseCode = urlConnection.getResponseCode();
InputStream inputStream;
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = urlConnection.getInputStream();
} else {
inputStream = urlConnection.getErrorStream();
}
return toString(inputStream);
}
/**
* Writes the content of the input stream to a <code>String<code>.
*/
private String toString(InputStream inputStream) throws IOException {
String lineToRead;
StringBuilder outputBuilder = new StringBuilder();
if (inputStream != null) {
BufferedReader reader =
new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while (null != (lineToRead = reader.readLine())) {
outputBuilder.append(lineToRead).append('\n');
}
}
return outputBuilder.toString();
}
}
+641
View File
@@ -0,0 +1,641 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator" content=
"HTML Tidy for Linux/x86 (vers 1st November 2003), see www.w3.org" />
<title>Google Base data API: Sample Applications</title>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
</head>
<body>
<P>Sample standalone applications accessing Google Base data API feeds</P>
<h2><a name="intro" id="intro"></a>Introduction</h2>The Google Base data Java
<a href='http://code.google.com/apis/base/sample-apps.html'>Client
Library</a> offers a nice object oriented abstraction for accessing the
Google Base data API. However, if you prefer to interact with the Google Base
data API servers directly, here are a few examples that will start you up.
<p>This document assumes you know Java programming (including some basic
knowledge of Http connections and SAX parsers) and that you are familiar with
<a href="http://code.google.com/apis/base/concepts.html">Google Base
concepts</a>. You need to understand the following concepts before you can
take full advantage of the sample applications:</p>
<ul>
<li><a href=
"http://code.google.com/apis/base/attrs-queries.html#queries">queries</a></li>
<li><a href=
"http://code.google.com/apis/base/attrs-queries.html#gbItemTypes">item
types</a></li>
<li><a href=
"http://code.google.com/apis/base/attrs-queries.html#gbAttrs">attributes</a></li>
</ul>
<p>This tutorial consists of stepping through 5 examples. The first example
shows how to connect to a Google Base data API feed and query data. The
second example extends this by showing how to parse the result and extract
data of interest from the feed. The third example introduces authentication
and demonstrates how to display your own items, rather than querying
snippets. The fourth example demonstrates how to insert your own item into
Google Base, while the last example shows how to update a previously inserted
item.</p>
<h2><a name="queryExample1" id="queryExample1"></a>Query Example 1 - query
the Google Base data API and display the result</h2>
<p><code>QueryExample1</code> is a simple Java application that runs from the
command line. It performs an unauthenticated query on the public <a href=
'http://code.google.com/apis/base/snippets-feed.html'>snippets feed</a>
(<code>/feeds/snippets</code>) and it dumps the query response to the console
(it won't look pretty!).</p>
<h3><a name="QE1run" id="QE1run"></a>Running
<code>QueryExample1</code></h3>Edit QueryExample1.java and fill in the
Compile and run the example using your favorite editor, or using the
command line:
<pre>
javac sample.gbase/basic/QueryExample1.java
java sample.gbase/basic/QueryExample1
</pre>The output (conveniently formatted) will look like:
<pre>
&lt;feed&gt;
&lt;id&gt;http://0.baseapitest.googlebase-api.jc.borg.google.com.:31911/base/feeds/snippets&lt;/id&gt;
&lt;updated&gt;2006-08-22T14:14:11.984Z&lt;/updated&gt;
&lt;title type="text"&gt;Items matching query: cars [item type : products]&lt;/title&gt;
&lt;link rel="alternate" type="text/html" href="http://base.google.com"/&gt;
&lt;link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://0.baseapitest.googlebase-api.jc.borg.google.com.:31911/base/feeds/snippets"/&gt;
&lt;link rel="self" type="application/atom+xml" href="http://0.baseapitest.googlebase-api.jc.borg.google.com.:31911/base/feeds/snippets?key=ABQ...9P2Y4A&amp;bq=cars+%5Bitem+type+%3A+products%5D"/&gt;
&lt;link rel="next" type="application/atom+xml" href="http://0.baseapitest.googlebase-api.jc.borg.google.com.:31911/base/feeds/snippets?start-index=26&amp;max-results=25&amp;key=ABQ...9P2Y4A&amp;bq=cars+%5Bitem+type+%3A+products%5D"/&gt;
&lt;generator version="1.0" uri="http://base.google.com"&gt;GoogleBase&lt;/generator&gt;
&lt;openSearch:totalResults&gt;278120&lt;/openSearch:totalResults&gt;
&lt;openSearch:itemsPerPage&gt;25&lt;/openSearch:itemsPerPage&gt;
&lt;entry&gt;
&lt;id&gt;http://0.baseapitest.googlebase-api.jc.borg.google.com.:31911/base/feeds/snippets/10062394959501653657&lt;/id&gt;
&lt;published&gt;2006-06-30T21:45:12.000Z&lt;/published&gt;
&lt;updated&gt;2006-07-28T00:58:14.000Z&lt;/updated&gt;
...
</pre>
<h3><a name="QE1stepThru" id="QE1stepThru"></a>Stepping through the
<code>QueryExample1</code> code</h3>
<p>At the very beginning we define the url of the feed we are connecting to
and the query that we are going to run:</p>
<pre>
private static final String SNIPPETS_FEED = "http://base.google.com/base/feeds/snippets";
private static final String QUERY = "cars [item type : products]";
</pre>Feel free to change the query to a more relevant or interesting one. Take
a look at the <a href=
'http://code.google.com/apis/base/query-lang-spec.htm'>Google Base Query
Language</a> description if you need some inspiration.
<p>After opening a connection on the snippets feed, we grab the connection's
input stream, and dump its content to the output, character by character:</p>
<pre>
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();
InputStream inputStream = httpConnection.getInputStream();
int ch;
while ((ch=inputStream.read()) &gt; 0) {
System.out.print((char)ch);
}
</pre>In the <code>main</code> method, all we do is create a
<code>QueryExample1</code> instance, and invoke its <code>displayItems</code>
method:
<pre>
public static void main(String[] args) throws IOException, SAXException,
ParserConfigurationException {
new QueryExample1().displayItems();
}
</pre>
<h2><a name="queryExample2" id="queryExample2"></a>Query Example 2 - query
the Google Base data API, parse the result and display the titles of returned
data items</h2>
<p>One major problem with <code>QueryExample1</code> is that it simply dumps
the items returned for the query to the console, and the result is barely
readable. In any real life application the query result would need to be
parsed, interpreted and relevant information should be extracted and
displayed to the user. In <code>QueryExample2</code> we demonstrate a
possible way of doing this by using a SAX parser to extract each data item's
title from the result, and display it to the console. Some very basic
understanding of how SAX parsers work is necessary in order to fully
understand this example.</p>
<p>One might argue that for this task we don't even need a SAX parser. We
could just search for all <code>&lt;title&gt;</code> tags in the result and
display the characters that they enclose. Unfortunately, that wouldn't work,
as the Atom response also has a <code>&lt;title&gt;</code> tag, which is the
title of the feed:</p>
<pre>
&lt;feed&gt;
...
&lt;title type="text"&gt;Items matching query: cars [item type : products]&lt;/title&gt;
...
&lt;entry&gt;
...
&lt;title type='text'&gt;Great care for sale&lt;/title&gt;
...
</pre>Atom does not mandate that the feed's <code>&lt;title&gt;</code> tag
should appear at a specific position inside the feed, so we need to make sure
we only display the <code>&lt;title&gt;</code> tags which are sub-elements of
an <code>&lt;entry&gt;</code> tag.<br />
<br />
<h3><a name="QE2run" id=
"QE2run"></a>Running<code>QueryExample2</code></h3>
Compile and run the example using your favorite editor, or using the
command line:
<pre>
javac sample.gbase/basic/QueryExample2.java
java sample.gbase/basic/QueryExample2
</pre>The output will look like:
<pre>
Item 1: Johnny Lightning MUSCLE CARS R8 1967 Chevelle SS
Item 2: Johnny Lightning MUSCLE CARS USA 2005 Ford GT
...
Item 25: The Cars movie Hinged tool Box Toy Organize lunch RARE
</pre>
<ul class="noindent"></ul>
<h3><a name="QE2stepThru" id="QE2stepThru"></a>Stepping through the
<code>QueryExample2</code> code</h3>
<p>Just as in the previous example, we first send the query to the Google
Base data API server, and obtain an <code>inputStream</code> containing the
response:</p>
<pre>
URL url = new URL(SNIPPETS_FEED + "?bq=" +
URLEncoder.encode(QUERY, "UTF-8"));
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = httpConnection.getInputStream();
</pre>
<p>We then use a standard SAX parser to parse the result. We obtain a
SAXParser instance using a SAXParserFactory:</p>
<pre>
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
</pre>We then parse the result <code>inputStream</code> using
<code>DisplayTitlesHandler</code>, our custom SAX event handler:
<pre>
parser.parse(inputStream, new DisplayTitlesHandler());
</pre>
<p><code>DisplayTitlesHandler</code> is derived from the no-op SAX parser,
<code>org.xml.sax.helpers.DefaultHandler</code>. The logic inside
<code>DisplayTitlesHandler</code> is pretty simple: we keep a stack of the
currently open XML tags and print all character data when we are inside a
<code>&lt;title&gt;</code> tag with a <code>&lt;entry&gt;</code> parent. The
<code>insideEntryTitle</code> flag is turned on each time we are inside a
<code>&lt;entry&gt;&lt;title&gt;</code> pair:</p>
<pre>
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals("title") &amp;&amp; xmlTags.peek().equals("entry")) {
insideEntryTitle = true;
System.out.print("Item " + ++itemNo + ": ");
}
xmlTags.push(qName);
}
</pre>Since <code>startElement</code> is invoked each time the SAX parser
encounters an opening XML tag, we switch on <code>insideEntryTitle</code> only
if the currently parsed opening tag is <code>&lt;title&gt;</code> and the tag
on the top of the stack is <code>&lt;entry&gt;</code> . We are also printing
here the "Item no: " messages, rather than in the <code>characters</code>
method, as <code>characters</code> can be called multiple times for different
chunks of the title's character data.<br />
<br />
The <code>endElement</code> method is invoked each time an XML tag closes.
All we need to do here is to remove the closing XML tag from the stack and,
in case the removed XML tag was an entry's title, flip
<code>insideEntryTitle</code> to false and go to a new line in the console,
in preparation for printing out the next title:
<pre>
public void endElement(String uri, String localName, String qName) throws SAXException {
xmlTags.pop();
if (insideEntryTitle) {
insideEntryTitle = false;
System.out.println();
}
}
</pre>The <code>characters</code> method is invoked when the parser encounters
character data inside an XML element. If we are inside the
<code>&lt;title&gt;</code> tag, that is, if the <code>insideEntryTitle</code>
flag is on, we display the current characters: <code>length</code> characters
from the <code>ch</code> array, starting with <code>start</code>. As noted
earlier, we can't use <code>println</code> here to get to a new line, as <code>
characters</code> can be invoked multiple times for different chunks of the
same title:
<pre>
public void characters(char[] ch, int start, int length) throws SAXException {
if (insideEntryTitle) {
System.out.print(new String(ch, start, length));
}
}
</pre>In the <code>main</code> method, all we do is create a
<code>QueryExample2</code> instance, and invoke its <code>displayItems</code>
method:
<pre>
public static void main(String[] args) throws IOException, SAXException, ParserConfigurationException {
new QueryExample2().displayItems();
}
</pre>
<h2><a name="queryExample3" id="queryExample3"></a>Query Example 3 -
Introducing authentication. Querying your own items using the items
feed.</h2>
<p>The previous two examples demonstrated how to query the Google Base data
API public feeds, also known as snippets. Snippets are accessible to anyone
without authentication. The downside is that snippets are the "indexable"
version of the items and can slightly differ from the original items. It can
also take a while for a newly inserted or updated item to show up in the
snippets feed. Therefore, if you need to change your own items, the Google
Base data API server exposes the customer-specific "items" feed. This feed is
very similar to the "snippets" feed, except that:</p>
<ul>
<li>it only allows you to query your own items, and thus you need to
authenticate in order to access it</li>
<li>there is no delay between updating an item and being able to display it
in the "items" feed</li>
</ul>Read <a href='http://code.google.com/apis/base/items-feed.html'>here</a>
more about the characteristics of the "items" feed. You will need a Google
Account email and password in order to run this example. If you don't have a
Google Account, sign up for one <a href=
'https://www.google.com/accounts/NewAccount'>here</a>.
<p>QueryExample3 connects to your "items" feed, and dumps your items to the
console, just as in <code>QueryExample1</code>. If you don't have any items
in Google Base yet, use <a href='#insertRun'>InsertExample</a> to insert one,
or go to the <a href='http://base.google.com/base/step1offer?hl=en_US'>Google
Base Provider Frontenac</a> and insert one.</p>
<h3><a name="QE3run" id="QE3run"></a>Running <code>QueryExample3</code></h3>
<ol>
<li>Edit QueryExample3.java and make the following changes:
<ul>
<li>Enter your Google Accounts email address and your password in the
<code>EMAIL</code> and <code>PASSWORD</code> static strings:
<pre>
private static final String EMAIL = "";
private static final String PASSWORD = "";
</pre>
</li>
</ul>
</li>
<li>Compile and run the example using your favorite editor, or the command
line:
<pre>
javac sample.gbase/basic/QueryExample3.java
java sample.gbase/basic/QueryExample3
</pre>
</li>
<li>The output will look like:
<pre>
&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;feed&gt;
&lt;id&gt;http://base.google.com/base/feeds/items&lt;/id&gt;
&lt;updated&gt;2006-08-22T12:00:00.000Z&lt;/updated&gt;
&lt;title type="text"&gt;Items matching query: [customer id(int):1870031]&lt;/title&gt;
...
</pre>
</li>
</ol>
<h3><a name="QE3stepThru" id="QE3stepThru"></a>Stepping through the
<code>QueryExample3</code> code</h3>
<p>As opposed to the previous examples, here we need to obtain an
authorization token by authenticating with the Google Accounts server. We
then use this authorization token to invoke <code>displayMyItems</code>:</p>
<pre>
public static void main(String[] args) throws IOException {
QueryExample3 queryExample = new QueryExample3();
String token = queryExample.authenticate();
new QueryExample3().displayMyItems(token);
}
</pre>
<p>We authenticate using <a href=
'http://code.google.com/apis/accounts/AuthForInstalledApps.html'>authentication
for installed applications</a>. The authentication procedure is simple. We
make a POST request to <code>AUTHENTICATION_URL</code>:</p>
<pre>
private static final String AUTHENTICATION_URL = "https://www.google.com/accounts/ClientLogin";
</pre>The POST request is constructed in <code>makeLoginRequest</code>, and it
looks like this:
<pre>
// POST /accounts/ClientLogin HTTP/1.0
// Content-type: application/x-www-form-urlencoded
// Email=johndoe@gmail.com&amp;Passwd=north23AZ&amp;service=gbase&amp;source=Insert Example
</pre><code>makeLoginRequest</code> sends the request to the Google Accounts
server and returns the response as a <code>String</code>. A successful response
will have the following structure:
<pre>
// HTTP/1.0 200 OK
// Server: GFE/1.3
// Content-Type: text/plain
// SID=DQAAAGgA...7Zg8CTN
// LSID=DQAAAGsA...lk8BBbG
// Auth=DQAAAGgA...dk3fA5N
</pre><code>authenticate</code> first obtains the authentication response from
<code>makeLoginRequest</code> and stores it in <code>postOutput</code>:
<pre>
String postOutput = null;
try {
URL url = new URL(AUTHENTICATION_URL);
postOutput = makeLoginRequest(url);
} catch (IOException e) {
System.out.println("Authentication error: " + e.toString());
}
</pre>It then tokenizes <code>postOutput</code>, and returns the next token
after "Auth", or <code>null</code> if the token is not found:
<pre>
StringTokenizer tokenizer = new StringTokenizer(postOutput, "=\n ");
String token = null;
while (tokenizer.hasMoreElements()) {
if (tokenizer.nextToken().equals("Auth")) {
if (tokenizer.hasMoreElements()) {
token = tokenizer.nextToken();
}
break;
}
}
</pre><br />
<br />
<p>The items are displayed in <code>displayMyItems</code>, which is very
similar to <code>displayItems</code> in QueryExample1, except that it injects
the authorization token in the Http header (as in this example):</p>
<pre>
connection.setRequestProperty("Authorization", "GoogleLogin auth=" + token);
</pre>
<h2><a name="insertExample" id="insertExample"></a>Insert Example - Adding
your own item to Google Base.</h2>
<p>The previous examples demonstrated how to query the Google Base data API
server, both for snippets (unauthenticated feeds) and for items
(authenticated feeds, containing a specific customer's items). Let's
demonstrate now how to add to Google Base all the cool stuff you have, so
that the world can see it. Again, you will need a Google Account email and
password in order to run this example. If you don't have a Google Account,
sign up for one <a href=
'https://www.google.com/accounts/NewAccount'>here</a>.</p>
<p>In this example we will connect to your "items" feed, and do an Http POST
operation (as opposed to GET, used for querying) to add a new item. The item
to be added is encoded in Atom format, and is defined a <code>String</code>
constant:</p>
<pre>
private static final String DATA_ITEM =
"&lt;?xml version=\'1.0\'?&gt;\n" +
"&lt;entry xmlns=\'http://www.w3.org/2005/Atom\' xmlns:g=\'http://base.google.com/ns/1.0\'&gt;\n" +
" &lt;category scheme=\'http://base.google.com/categories/itemtypes\' term=\'Products\'/&gt;\n" +
" &lt;g:item_type type=\'text\'&gt;Products&lt;/g:item_type&gt;\n" +
" &lt;title type=\'text\'&gt;My cool car is for sale&lt;/title&gt;" +
" &lt;content type=\'xhtml\'&gt;Light pink, yellow seats.&lt;/content&gt;" +
"&lt;/entry&gt;";
</pre>It's a very simple item, consisting only of an item type ("products", in
our case), a title and a content (description). Feel free to change these
fields to contain your personalized items or to add new attributes and labels
(use the responses dumped by <code>QueryExample1</code> and
<code>QueryExample3</code> as an inspiration for adding new attributes).
<h3><a name="insertRun" id="insertRun"></a>Running
<code>InsertExample</code></h3>
<ol>
<li>Edit InsertExample.java and make the following changes:
<ul>
<li>Enter your Google Accounts email address and your password in the
<code>EMAIL</code> and <code>PASSWORD</code> static strings:
<pre>
private static final String EMAIL = "";
private static final String PASSWORD = "";
</pre>
</li>
</ul>
</li>
<li>Compile and run the example using your favorite editor, or the command
line:
<pre>
javac sample.gbase/basic/InsertExample.java
java sample.gbase/basic/InsertExample
</pre>
</li>
<li>The output will look like:
<pre>
Obtained authorization token: DQAAAGgA...dk3fA5N
&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;entry&gt;
&lt;id&gt;http://base.google.com/base/feeds/items/16024998325761524417&lt;/id&gt;
&lt;published&gt;2006-08-23T15:18:55.184Z&lt;/published&gt;
&lt;updated&gt;2006-08-23T15:18:55.184Z&lt;/updated&gt;
&lt;category scheme="http://base.google.com/categories/itemtypes" term="Products"/&gt;
&lt;title type="text"&gt;My cool car is for sale&lt;/title&gt;
&lt;content type="xhtml"&gt;Light pink, yellow seats.&lt;/content&gt;
&lt;link rel="self" type="application/atom+xml" href="http://base.google.com/base/feeds/items/16024998325761524417"/&gt;
&lt;link rel="edit" type="application/atom+xml" href="http://base.google.com/base/feeds/items/16024998325761524417"/&gt;
&lt;g:item_type type="text"&gt;Products&lt;/g:item_type&gt;
&lt;/entry&gt;
</pre>
</li>
</ol>
<h3><a name="InsertStepThru" id="InsertStepThru"></a>Stepping through the
<code>InsertExample</code> code</h3>
<p>We use the same feed as in QueryExample3.java to insert the item:</p>
<pre>
private static final String ITEMS_FEED = "http://base.google.com/base/feeds/items";
</pre>Authentication is also performed as in <a href=
'#QE3stepThru'>QueryExample3</a>, using <code>makeLoginRequest</code> to
request an authorization token and <code>authenticate</code> to parse the
authentication response. The insertion of the new data item is done in
<code>postItem</code>, which connects to the items feed:
<pre>
HttpURLConnection connection = (HttpURLConnection)(new URL(ITEMS_FEED)).openConnection();
</pre>Once the connection is created, we need to set its properties: the Http
request method, the content type of the information that is being posted, the
authorization header:
<pre>
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/atom+xml");
connection.setRequestProperty("Authorization", "GoogleLogin auth=" + token);
</pre>We then obtain the output stream of the connection and dump
<code>DATA_ITEM</code> into it:
<pre>
OutputStream outputStream = connection.getOutputStream();
outputStream.write(DATA_ITEM.getBytes());
outputStream.close();
</pre>The rest of the method is already familiar: we obtain the response code
and print out to the console the contents of the input stream corresponding to
the response code.<br />
<br />
<h2><a name="updateExample" id="updateExample"></a>Update Example - Modifying
your Google Base items.</h2>
<p>The previous examples demonstrated how to query the Google Base data API
server both for authenticated and unauthenticated feeds and how to insert a
new item. We will combine this knowledge in order to demonstrate how to
update an already existing item. Again, you will need a Google Account email
and password in order to run this example. If you don't have a Google
Account, sign up for one <a href=
'https://www.google.com/accounts/NewAccount'>here</a>. This example does not
introduce important new concepts, but rather uses all the essentials that
have been presented in the previous examples: we will authenticate, insert an
item, and then update the inserted item by adding a new label to it.</p>
<h3><a name="updateRun" id="updateRun"></a>Running
<code>UpdateExample</code></h3>
<ol>
<li>Edit UpdateExample.java and make the following changes:
<ul>
<li>Enter your Google Accounts email address and your password in the
<code>EMAIL</code> and <code>PASSWORD</code> static strings:
<pre>
private static final String EMAIL = "";
private static final String PASSWORD = "";
</pre>
</li>
</ul>
</li>
<li>Compile and run the example using your favorite editor, or the command
line:
<pre>
javac sample.gbase/basic/UpdateExample.java
java sample.gbase/basic/UpdateExample
</pre>
</li>
<li>The output will look like:
<pre>
Obtained authorization token: DQAAAGgA...dk3fA5N
Posting item:
&lt;?xml version='1.0'?&gt;
&lt;entry xmlns='http://www.w3.org/2005/Atom' xmlns:g='http://base.google.com/ns/1.0'&gt;
&lt;category scheme='http://base.google.com/categories/itemtypes' term='Products'/&gt;
&lt;g:item_type type='text'&gt;Products&lt;/g:item_type&gt;
&lt;title type='text'&gt;My cool car is for sale&lt;/title&gt;
&lt;content type='xhtml'&gt;Light pink, yellow seats.&lt;/content&gt;
&lt;/entry&gt;
Updating item: http://base.google.com/base/feeds/items/18020038538902937385
&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;entry&gt;
&lt;id&gt;http://base.google.com/base/feeds/items/18020038538902937385&lt;/id&gt;
&lt;updated&gt;2006-08-23T16:20:21.601Z&lt;/updated&gt;
&lt;category scheme="http://base.google.com/categories/itemtypes" term="Products"/&gt;
&lt;title type="text"&gt;My cool car is for sale&lt;/title&gt;
&lt;content type="xhtml"&gt;Light pink, yellow seats.&lt;/content&gt;
&lt;link rel="self" type="application/atom+xml" href="http://base.google.com/base/feeds/items/18020038538902937385"/&gt;
&lt;link rel="edit" type="application/atom+xml" href="http://base.google.com/base/feeds/items/18020038538902937385"/&gt;
&lt;g:item_type type="text"&gt;Products&lt;/g:item_type&gt;
&lt;/entry&gt;
</pre>
</li>
</ol>
<h3><a name="UpdateStepThru" id="UpdateStepThru"></a>Stepping through the
<code>UpdateExample</code> code</h3>
<p>The <code>main</code> method of <code>UpdateExample</code> provides a good
outline of the example's structure:</p>
<pre>
public static void main(String[] args) throws MalformedURLException, IOException {
UpdateExample updateExample = new UpdateExample();
String token = updateExample.authenticate();
System.out.println("Obtained authorization token: " + token);
System.out.println("Posting item:\n" + DATA_ITEM);
String itemUrl = updateExample.extractItemUrlFromResponse(
updateExample.postItem(token));
System.out.println("Updating item: " + itemUrl);
String updateResponse = updateExample.updateItem(token, itemUrl);
System.out.println(updateResponse);
}
</pre>Just like in the previous examples, we start by authenticating and
obtaining an authorization token using the <code>authenticate</code> method:
<pre>
String token = updateExample.authenticate();
</pre>We then insert <code>DATA_ITEM</code> using <code>postItem</code>:
<pre>
String itemUrl = updateExample.extractItemUrlFromResponse(
updateExample.postItem(token));
</pre>In the line above, we pass the output of the post operation
to<code>extractItemUrlFromResponse</code> (rather than dumping it out to the
console), which extracts the inserted item's id:
<pre>
&lt;id&gt;http://base.google.com/base/feeds/items/18020038538902937385&lt;/id&gt;
</pre>We assume that the item's id is surrounded by the first
&lt;id&gt;&lt;/id&gt; tags; this is true for the Google Base data API servers,
but it's not enforced by the Atom protocol. See how the title gets parsed in
<a href='#QE2stepThru'>Query Example 2</a>, for a superior approach on parsing
the item's title.
<p>Once <code>DATA_ITEM</code> is successfully inserted, we replace it with
<code>NEW_DATA_ITEM</code> using <code>updateItem</code>. Updating an item is
very similar to inserting a new one - in fact so similar that both operations
can be performed by the same method: <code>makeHttpRequest</code>.
<code>makeHttpRequest</code> receives as parameters the authorization token,
the url to connect to, the item to be inserted or posted, the http method to
use (this will be POST for inserting, and PUT for deleting) and the response
code to expect in case of a successful operation (HTTP_CREATED in case of
insert, HTTP_OK in case of update). Thus, <code>postItem</code> will contain
a simple invocation to <code>makeHttpRequest</code>:</p>
<pre>
public String postItem(String token) throws IOException {
return makeHttpRequest(token, ITEMS_FEED, NEW_DATA_ITEM, "POST", HttpURLConnection.HTTP_CREATED);
}
</pre>Similarly, <code>updateItem</code> invokes <code>makeHttpRequest</code>
with slightly different parameters:
<pre>
public String updateItem(String token, String itemUrl) throws MalformedURLException, IOException {
return makeHttpRequest(token, itemUrl, NEW_DATA_ITEM, "PUT", HttpURLConnection.HTTP_OK);
}
</pre>
</body>
</html>
@@ -0,0 +1,40 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
/**
* Execute batch operations.
* This command is called from {@link sample.gbase.cmdline.CustomerTool}.
*/
class BatchCommand extends Command {
public void execute() throws Exception {
Service service = createService();
Service.GDataRequest request =
service.createBatchRequest(
fixEditUrl(urlFactory.getItemsBatchFeedURL()));
inputRawRequest(request);
// Send the request (HTTP POST)
request.execute();
// Save the response
outputRawResponse(request);
}
}
@@ -0,0 +1,250 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.api.gbase.client.FeedURLFactory;
import com.google.api.gbase.client.GoogleBaseService;
import com.google.gdata.client.Service;
import com.google.gdata.util.AuthenticationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
/**
* One command that is part of CustomerTool.
* This class contains code that is common to all
* commands. It deals, in particular, with the creation
* of the GData service object.
*/
abstract class Command {
/**
* URL of the google authentication server to use for log in.
*/
private static final String DEFAULT_AUTH_HOSTNAME = "www.google.com";
/**
* Username for google base (e-mail address).
*/
protected String username;
/**
* The user's password.
*/
protected String password;
/**
* Base url of the google base server.
*/
protected FeedURLFactory urlFactory = FeedURLFactory.getDefault();
/**
* Base url of the google authentication server.
*/
private String authenticationServer = DEFAULT_AUTH_HOSTNAME;
/**
* Protocol (http or https) to use to connect to the authentication server.
*/
private String authenticationProtocol = "https";
/**
* Developer key used for identification against the Google Base data API
* servers.
*/
private String key;
/**
* Enables dry-run mode for edit operations.
*/
private boolean dryRun;
/**
* Executes the command.
*
* Call this method only after setting the username and password.
*/
public abstract void execute() throws Exception;
/**
* Sets the username, which is required for {@link #execute()} to work.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Sets the password, which is required for {@link #execute()} to work.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Sets the Url of the google base server to connect to.
*/
public void setGoogleBaseServerUrl(String url) throws MalformedURLException {
this.urlFactory = new FeedURLFactory(url);
}
/**
* Sets the name of the google authentication server to connect to.
*/
public void setAuthenticationServerUrl(String urlString)
throws MalformedURLException {
URL url = new URL(urlString);
this.authenticationProtocol = url.getProtocol();
this.authenticationServer = url.getHost();
if (url.getPort() != -1) {
this.authenticationServer += ":" + url.getPort();
}
}
public void setKey(String key) {
this.key = key;
}
/**
* Makes sure username and password have been set.
*/
public boolean hasAllIdentificationInformation() {
return username != null && password != null;
}
/**
* Creates the service and sets the username and password for
* authentication.
*
* @return the GData service object to use
* @throws com.google.gdata.util.AuthenticationException
* if authentication failed
*/
protected GoogleBaseService createService()
throws AuthenticationException {
GoogleBaseService service =
new GoogleBaseService("Google.-CustomerTool-1.0",
key,
authenticationProtocol,
authenticationServer);
service.setUserCredentials(username, password);
return service;
}
/**
* Gets the URL of the customer feed.
*
* The feed contains only the items uploaded by this
* specific customer. This is also the feed that is
* used to upload customer data.
*
* @return the url to the customer feed
*/
protected URL getCustomerFeedURL() throws MalformedURLException {
return urlFactory.getItemsFeedURL();
}
/**
* Writes the response (XML feed) to standard output.
*/
protected void outputRawResponse(Service.GDataRequest request)
throws IOException {
InputStream responseStream = request.getResponseStream();
try {
copyStreamContent(responseStream, System.out);
} finally {
responseStream.close();
}
System.out.println();
}
/**
* Reads data from in input stream and write it into an
* output stream.
*/
protected void copyStreamContent(InputStream in, OutputStream out)
throws IOException {
byte[] buffer = new byte[1024];
int l;
while ( (l=in.read(buffer)) > 0 ) {
out.write(buffer, 0, l);
}
}
/**
* Reads data from standard input and use it in the request.
*
* The data must be the XML feed appropriate for the command.
* For update, get or insert it should be one Atom XML entry.
*/
protected void inputRawRequest(Service.GDataRequest request)
throws IOException {
OutputStream outputStream = request.getRequestStream();
try {
copyStreamContent(System.in, outputStream);
} finally {
outputStream.close();
}
}
/**
* Puts the command in dry-run mode, in which nothing will
* really happen on the server.
*
* @param dryRun
*/
public void setDryRun(boolean dryRun) {
this.dryRun = dryRun;
}
/**
* Builds an edit URL from a string, adding the dry-run parameter
* if necessary.
*
* This method is not applicable to query URLs, for which the dry-run
* parameter is not supported.
*
* @param url the original url
* @return the same URL with maybe some parameters
* @throws MalformedURLException
*/
protected URL fixEditUrl(URL url) throws MalformedURLException {
return fixEditUrl(url.toExternalForm());
}
/**
* Builds an edit URL from a string, adding the dry-run parameter
* if necessary.
*
* This method is not applicable to query URLs, for which the dry-run
* parameter is not supported.
*
* @param url the original url, as a string
* @return the same URL with maybe some parameters
* @throws MalformedURLException
*/
protected URL fixEditUrl(String url) throws MalformedURLException {
if (dryRun) {
char separator = url.contains("?") ? '&' : '?';
url = url + separator + "dry-run=true";
}
return new URL(url);
}
}
@@ -0,0 +1,316 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import java.net.MalformedURLException;
import java.util.Arrays;
/**
* Creates and initializes commands given command-line
* arguments.
*
* This class is totally useless if you want to understand
* how the API works. All it does is parse command-line
* arguments and call some setters on the different <code>*Command</code>
* objects. Have a look at the <code>*Command</code> classes instead.
*/
class CommandFactory {
/**
* All commands available in this system, for producing error messages.
*/
private static final String ALL_COMMANDS =
"query, get, update, insert, delete, batch";
/**
* Creates a {@link Command} object and initializes it using
* command-line arguments.
*
* @param args command-line arguments
* @return a new Command object, properly initialized and ready to use
*/
public static Command createCommand(String[] args) {
if (args.length == 0) {
throw error("Please give first the command you want to run (" +
ALL_COMMANDS + ") then the parameters for that " +
"command.");
}
String commandName = args[0];
if ("query".equals(commandName)) {
return createQueryCommand(args);
} else if("insert".equals(commandName)) {
return createInsertCommand(args);
} else if("update".equals(commandName)) {
return createUpdateCommand(args);
} else if("delete".equals(commandName)) {
return createDeleteCommand(args);
} else if("get".equals(commandName)) {
return createGetCommand(args);
} else if("batch".equals(commandName)) {
return createBatchCommand(args);
} else if("query-media".equals(commandName)) {
return createQueryMediaCommand(args);
} else if("insert-media".equals(commandName)) {
return createInsertMediaCommand(args);
} else if("update-media".equals(commandName)) {
return createUpdateMediaCommand(args);
} else if("delete-media".equals(commandName)) {
return createDeleteMediaCommand(args);
} else if("get-media".equals(commandName)) {
return createGetMediaCommand(args);
} else {
throw error("Unknown command: " + commandName +
". Available commands: " + ALL_COMMANDS);
}
}
/**
* Creates and initializes a {@link QueryCommand}.
*/
private static Command createQueryCommand(String[] args) {
QueryCommand command = new QueryCommand();
args = parseAndSetCommonArguments(command, args);
if (args.length == 1) {
command.setQuery(args[0]);
} else if (args.length > 1) {
throw error("Expected at most one query argument, got " + args.length);
}
return command;
}
/**
* Creates and initializes a {@link InsertCommand}.
*/
private static Command createInsertCommand(String[] args) {
InsertCommand command = new InsertCommand();
args = parseAndSetCommonArguments(command, args);
expectNoMoreArguments(args);
return command;
}
/**
* Creates and initializes a {@link BatchCommand}.
*/
private static Command createBatchCommand(String[] args) {
BatchCommand command = new BatchCommand();
args = parseAndSetCommonArguments(command, args);
expectNoMoreArguments(args);
return command;
}
/**
* Creates and initializes a {@link UpdateCommand}.
*/
private static Command createUpdateCommand(String[] args) {
UpdateCommand command = new UpdateCommand();
args = parseAndSetCommonArguments(command, args);
command.setItemId(getItemId(args));
return command;
}
/**
* Creates and initializes a {@link DeleteCommand}.
*/
private static Command createDeleteCommand(String[] args) {
DeleteCommand command = new DeleteCommand();
args = parseAndSetCommonArguments(command, args);
command.setItemId(getItemId(args));
return command;
}
/**
* Creates and initializes a {@link GetCommand).
*/
private static Command createGetCommand(String[] args) {
GetCommand command = new GetCommand();
args = parseAndSetCommonArguments(command, args);
command.setItemId(getItemId(args));
return command;
}
/**
* Creates and initializes a {@link QueryMediaCommand}.
*/
private static Command createQueryMediaCommand(String[] args) {
QueryMediaCommand command = new QueryMediaCommand();
args = parseAndSetCommonArguments(command, args);
if (args.length == 1) {
command.setItemMediaUrl(args[0]);
} else {
throw error("Expected [item_media_feed_url], got " + Arrays.toString(args));
}
return command;
}
/**
* Creates and initializes a {@link InsertMediaCommand}.
*/
private static Command createInsertMediaCommand(String[] args) {
InsertMediaCommand command = new InsertMediaCommand();
args = parseAndSetCommonArguments(command, args);
if (args.length < 3 || args.length > 4) {
throw error("Expected four arguments: [item_media_feed_url attachment_path_to_file " +
"attachment_mime_type caption?], got " + Arrays.toString(args));
}
command.setItemMediaUrl(args[0]);
command.setAttachmentFile(args[1]);
command.setAttachmentMimeType(args[2]);
if (args.length == 4) {
command.setCaption(args[3]);
}
return command;
}
/**
* Creates and initializes a {@link UpdateMediaCommand}.
*/
private static Command createUpdateMediaCommand(String[] args) {
UpdateMediaCommand command = new UpdateMediaCommand();
args = parseAndSetCommonArguments(command, args);
command.setAttachmentId(getItemId(args));
return command;
}
/**
* Creates and initializes a {@link DeleteMediaCommand}.
*/
private static Command createDeleteMediaCommand(String[] args) {
DeleteMediaCommand command = new DeleteMediaCommand();
args = parseAndSetCommonArguments(command, args);
command.setAttachmentId(getItemId(args));
return command;
}
/**
* Creates and initializes a {@link GetMediaCommand).
*/
private static Command createGetMediaCommand(String[] args) {
GetMediaCommand command = new GetMediaCommand();
args = parseAndSetCommonArguments(command, args);
command.setAttachmentId(getItemId(args));
return command;
}
/**
* Get the item id from the command line and make sure it's the
* only argument left.
*
* @param args command line arguments left over from
* {@link #parseAndSetCommonArguments(Command, String[])}
* @return the item ID (an url)
*/
private static String getItemId(String[] args) {
if (args.length != 1) {
throw error("Expected one argument after the command name: " +
"the ID (url) of the item to delete.");
}
return args[0];
}
/**
* Parse the command line and initialize things that are common
* to all {@link Command}s.
*
* @param command the command object
* @param args command-line arguments (0 is the command name)
* @return the arguments that are left that haven't been parsed
* by this method, or an empty array if there's nothing left
*/
private static String[] parseAndSetCommonArguments(Command command,
String[] args) {
// Start at the first argument after the command name
int current = 1;
while (current < args.length && args[current].startsWith("-")) {
String option = args[current];
current++;
if ( current >= args.length) {
throw error("expected an argument after option " + option);
}
if ("--dry_run".equals(option)) {
command.setDryRun(true);
continue;
}
String value = args[current];
current++;
if ("--user".equals(option)) {
command.setUsername(value);
} else if("--password".equals(option)) {
command.setPassword(value);
} else if("--url".equals(option)) {
try {
command.setGoogleBaseServerUrl(value);
} catch(MalformedURLException e) {
throw error("Value for --url should be a valid URL: " +
e.getMessage());
}
} else if("--auth".equals(option)) {
try {
command.setAuthenticationServerUrl(value);
} catch(MalformedURLException e) {
throw error("Value for --auth should be a valid http or https " +
"URL: " + e.getMessage());
}
} else if("--key".equals(option)) {
command.setKey(value);
} else {
throw error("unexpected option: " + option);
}
}
if (!command.hasAllIdentificationInformation()) {
throw error("You must input all two required parameters: " +
"--user email --password password ");
}
// leave the rest for the command
String[] retval = new String[args.length-current];
System.arraycopy(args, current, retval, 0, retval.length);
return retval;
}
private static void expectNoMoreArguments(String[] args) {
if (args.length > 0) {
throw error("Expected no more arguments, got instead " + args[0]);
}
}
private static IllegalArgumentException error(String message) {
return new IllegalArgumentException("Error: wrong arguments. " + message);
}
}
@@ -0,0 +1,74 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.util.ServiceException;
import com.google.api.gbase.client.ServiceErrors;
import com.google.api.gbase.client.ServiceError;
import java.util.List;
/**
* An example tool that helps manage customer items on
* Google Base using Google data API.
*
* This tool deals directly with XML feeds. If you would
* like to use a higher-level API, have a look at QueryExample.
*
* Have a look at the different <code>*Command</code> classes for some more
* interesting code.
*/
public class CustomerTool {
public static void main(String[] args) throws Exception {
Command command = CommandFactory.createCommand(args);
try {
command.execute();
} catch (ServiceException e) {
/* Display the error message sent by the server, if it
* is available. A real application would need to parse
* the body (as HTML or XML, depending on e.getContentType())
* and display it nicely.
*/
StringBuffer message = new StringBuffer("Response code:");
ServiceErrors errors = new ServiceErrors(e);
if (e.getHttpErrorCodeOverride() > 0) {
message.append(" ");
message.append(e.getHttpErrorCodeOverride());
}
message.append(" ");
message.append(e.getMessage());
System.err.println(message);
List<? extends ServiceError> allErrors = errors.getAllErrors();
for (ServiceError error: allErrors) {
String field = error.getField();
StringBuffer buffer = new StringBuffer();
buffer.append(" ");
if (field != null) {
buffer.append("in field '");
buffer.append(field);
buffer.append("'");
buffer.append(": ");
}
buffer.append(error.getReason());
System.err.println(buffer);
}
System.exit(10);
}
}
}
@@ -0,0 +1,43 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
/**
* Deletes an item.
*/
public class DeleteCommand extends Command {
private String itemId;
public void execute() throws Exception {
Service service = createService();
Service.GDataRequest request =
service.createDeleteRequest(fixEditUrl(itemId));
// Send the request (HTTP DELETE)
request.execute();
System.out.println("Item deleted successfully.");
}
/** Sets the Id of the item, which is also its URL. */
public void setItemId(String itemId) {
this.itemId = itemId;
}
}
@@ -0,0 +1,44 @@
/* Copyright (c) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
/**
* Deletes a media attachment from an item.
* This command is called from {@link CustomerTool}.
*/
public class DeleteMediaCommand extends Command {
private String attachmentId;
@Override
public void execute() throws Exception {
Service service = createService();
Service.GDataRequest request =
service.createDeleteRequest(fixEditUrl(attachmentId));
// Send the request (HTTP DELETE)
request.execute();
System.out.println("Item deleted successfully.");
}
/** Sets the Id of the media attachment, which is also its URL. */
public void setAttachmentId(String attachmentId) {
this.attachmentId = attachmentId;
}
}
@@ -0,0 +1,111 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.api.gbase.client.FeedURLFactory;
import com.google.api.gbase.client.GoogleBaseService;
import com.google.api.gbase.client.ServiceError;
import com.google.api.gbase.client.ServiceErrors;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
/**
* Utility class that allows creating simple Example Tools.
*
* Contains methods for parsing the arguments and for printing errors.
*/
public abstract class Example {
protected static FeedURLFactory urlFactory = FeedURLFactory.getDefault();
protected static GoogleBaseService service;
/**
* Parses the arguments, creates a FeedURLFactory and a GoogleBaseService.
* @return the remaining arguments
*/
public static String[] init(String[] args, String applicationName)
throws IOException {
String baseUrl = null;
int argsIndex = 0;
while (argsIndex < args.length && args[argsIndex].startsWith("-")) {
String arg = args[argsIndex];
argsIndex++;
if ( argsIndex >= args.length) {
throw new IllegalArgumentException("Expected a parameter value " +
"after " + arg);
}
String value = args[argsIndex];
argsIndex++;
if ("--url".equals(arg)) {
baseUrl = value;
} else if("--key".equals(arg)) {
// This parameter used to contain the developer key.
// It is still accepted so as not to break scripts that used it, but
// it is now ignored.
} else {
throw new IllegalArgumentException("unknown parameter: " + arg);
}
}
if(baseUrl != null) {
urlFactory = new FeedURLFactory(baseUrl);
}
// service.query does a GET on the url above and parses the result,
// which is an ATOM feed with some extensions (called the Google Base
// data API items feed).
service = new GoogleBaseService(applicationName);
if (argsIndex > 0) {
String[] newargs = new String[args.length - argsIndex];
System.arraycopy(args, argsIndex, newargs, 0, newargs.length);
args = newargs;
}
return args;
}
/**
* Prints an error message returned by the server, if any.
*
* @param e an exception that may contain an error message from the server
*/
protected static void printServiceException(ServiceException e) {
System.err.print("Error");
if (e.getHttpErrorCodeOverride() > 0) {
System.err.print(e.getHttpErrorCodeOverride());
}
System.err.print(": ");
System.err.println(e.getMessage());
ServiceErrors errors = new ServiceErrors(e);
for (ServiceError error: errors.getAllErrors()) {
String field = error.getField();
System.err.print(" ");
if (field != null) {
System.err.print("in field '");
System.err.print(field);
System.err.print("' ");
}
System.err.println(error.getReason());
}
}
}
@@ -0,0 +1,45 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
import java.net.URL;
/**
* Displays one item.
*/
public class GetCommand extends Command {
private String itemId;
public void execute() throws Exception {
Service service = createService();
Service.GDataRequest request =
service.createEntryRequest(new URL(itemId));
// Send the request (HTTP GET)
request.execute();
// Save the response
outputRawResponse(request);
}
/** Sets the id/URL of the item to display. */
public void setItemId(String itemId) {
this.itemId = itemId;
}
}
@@ -0,0 +1,47 @@
/* Copyright (c) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
import java.net.URL;
/**
* Displays the media entry (meta-data) of one attachment.
* This command is called from {@link CustomerTool}.
*/
public class GetMediaCommand extends Command {
private String attachmentId;
@Override
public void execute() throws Exception {
Service service = createService();
Service.GDataRequest request =
service.createEntryRequest(new URL(attachmentId));
// Send the request (HTTP GET)
request.execute();
// Save the response
outputRawResponse(request);
}
/** Sets the id/URL of the media attachment to display. */
public void setAttachmentId(String attachmentId) {
this.attachmentId = attachmentId;
}
}
@@ -0,0 +1,40 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
/**
* Inserts a new Item into Google Base items.
* This command is called from {@link CustomerTool}.
*/
class InsertCommand extends Command {
public void execute() throws Exception {
Service service = createService();
Service.GDataRequest request =
service.createInsertRequest(fixEditUrl(getCustomerFeedURL()));
inputRawRequest(request);
// Send the request (HTTP POST)
request.execute();
// Save the response
outputRawResponse(request);
}
}
@@ -0,0 +1,83 @@
/* Copyright (c) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
import com.google.gdata.client.Service.GDataRequest;
import com.google.gdata.data.media.MediaFileSource;
import com.google.gdata.data.media.MediaSource;
import com.google.gdata.util.ContentType;
import java.io.File;
import java.net.URL;
/**
* Inserts a new media attachment to a Google Base item, by using
* a media binary POST. See
* {@link com.google.api.gbase.client.GoogleBaseService#insert(URL, Class, MediaSource)}
* for an easier way to do a media binary POST, or look at
* {@link com.google.api.gbase.client.GoogleBaseService#insert(URL, com.google.gdata.data.BaseEntry)}
* for an easier way to insert an attachment together with the media entry (meta-data) describing it.
* This command is called from {@link CustomerTool}.
*/
class InsertMediaCommand extends Command {
private String itemMediaUrl;
private String attachmentFile;
private String attachmentMimeType;
private String caption;
@Override
public void execute() throws Exception {
MediaFileSource media = new MediaFileSource(new File(attachmentFile), attachmentMimeType);
Service service = createService();
Service.GDataRequest request = service.createRequest(GDataRequest.RequestType.INSERT,
new URL(itemMediaUrl), new ContentType(attachmentMimeType));
if (caption != null) {
request.setHeader("Slug", caption);
}
MediaSource.Output.writeTo(media, request.getRequestStream());
// Send the request (HTTP POST)
request.execute();
// Save the response
outputRawResponse(request);
}
/** Set the url of the media feed for the item to insert the attachment into. */
public void setItemMediaUrl(String itemMediaUrl) {
this.itemMediaUrl = itemMediaUrl;
}
/** Sets the path to the file on the disk containing the attachment to upload. */
public void setAttachmentFile(String attachmentFile) {
this.attachmentFile = attachmentFile;
}
/** Sets the mime-type of the attachment. */
public void setAttachmentMimeType(String attachmentMimeType) {
this.attachmentMimeType = attachmentMimeType;
}
/** Sets the caption (title) of the attachment. */
public void setCaption(String caption) {
this.caption = caption;
}
}
@@ -0,0 +1,178 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.api.gbase.client.GoogleBaseAttributeId;
import com.google.api.gbase.client.GoogleBaseEntry;
import com.google.api.gbase.client.GoogleBaseFeed;
import com.google.api.gbase.client.GoogleBaseQuery;
import com.google.api.gbase.client.ItemTypeDescription;
import com.google.api.gbase.client.MetadataEntryExtension;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
import java.net.URL;
/**
* This class demonstrates how to retrieve Google Base item types
* using the client library of the Google Base data API.
*
* The tool implemented by this class will connect to Google Base,
* run the request and display some results.
*/
public class ItemTypesExample extends Example {
/**
* Runs the example.
*/
public static void main(String[] args) throws IOException, ServiceException {
String locale = null;
String itemType = null;
args = init(args, "Google-ItemTypesExample-1.0");
if (args.length == 0) {
// nothing to do
} else if (args.length == 1) {
locale = args[0];
} else if (args.length == 2) {
locale = args[0];
itemType = args[1];
} else {
System.err.println("Invalid argument count.");
System.err.println("Expected either two arguments, to get an itemtype:");
System.err.println(" locale itemtype");
System.err.println("or one argument, to get the itemtypes of a locale:");
System.err.println(" locale");
System.err.println("or no argument, to get the locales.");
System.exit(1);
}
if (locale == null) {
queryLocales();
} else {
if (itemType == null) {
queryItemTypes(locale);
} else {
queryItemType(locale, itemType);
}
}
}
/**
* Retrieves and prints the locales.
*
*/
private static void queryLocales()
throws IOException, ServiceException {
// Create a query URL
URL url = urlFactory.getLocalesFeedURL();
GoogleBaseQuery query = new GoogleBaseQuery(url);
// Display the URL generated by the API
System.out.println("Sending request to: " + query.getUrl());
try {
GoogleBaseFeed feed = service.query(query);
// Print the locales
for (GoogleBaseEntry entry : feed.getEntries()) {
System.out.println(entry.getTitle().getPlainText());
}
} catch (ServiceException e) {
printServiceException(e);
}
}
/**
* Retrieves and prints the item types Google suggests for a locale.
*
* @param locale the locale to be analysed
*/
private static void queryItemTypes(String locale)
throws IOException, ServiceException {
// Create a query URL from the given arguments
URL url = urlFactory.getItemTypesFeedURL(locale);
GoogleBaseQuery query = new GoogleBaseQuery(url);
// Display the URL generated by the API
System.out.println("Sending request to: " + query.getUrl());
try {
GoogleBaseFeed feed = service.query(query);
// Print the item types
printItemTypeFeed(feed);
} catch (ServiceException e) {
printServiceException(e);
}
}
/**
* Retrieves and prints the attribute ids Google suggests for an item type of
* a locale.
*
* @param locale the locale of the item type
* @param itemType the item type to be analysed
*/
private static void queryItemType(String locale, String itemType)
throws IOException, ServiceException {
// Create a query URL from the given arguments
URL url = urlFactory.getItemTypesEntryURL(locale, itemType);
// Display the URL generated by the API
System.out.println("Sending request to: " + url);
try {
GoogleBaseEntry entry = service.getEntry(url);
// Print the item type
printItemTypeEntry(entry);
} catch (ServiceException e) {
printServiceException(e);
}
}
/**
* Prints each itemtype item in the feed to the output.
* Uses {@link #printItemTypeEntry(GoogleBaseEntry)}.
*
* @param feed a Google Base data API itemtypes feed
*/
private static void printItemTypeFeed(GoogleBaseFeed feed) {
if (feed.getTotalResults() == 0) {
System.out.println("No matches.");
return;
}
for (GoogleBaseEntry entry : feed.getEntries()) {
printItemTypeEntry(entry);
}
}
/**
* Prints the name and the recommended attribute names of
* an itemtype GoogleBaseEntry item.
*
* @param entry a Google Base data API itemtype entry
*/
private static void printItemTypeEntry(GoogleBaseEntry entry) {
MetadataEntryExtension metadata = entry.getGoogleBaseMetadata();
ItemTypeDescription itemTypeDescription = metadata.getItemTypeDescription();
System.out.println(itemTypeDescription.getName() + " - " + entry.getId());
for (GoogleBaseAttributeId attrId : itemTypeDescription.getAttributeIds()) {
System.out.println(attrId.getName() +
" (" + attrId.getType().getName() + ")");
}
}
}
@@ -0,0 +1,118 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.api.gbase.client.AttributeHistogram;
import com.google.api.gbase.client.GoogleBaseEntry;
import com.google.api.gbase.client.GoogleBaseFeed;
import com.google.api.gbase.client.GoogleBaseQuery;
import com.google.api.gbase.client.MetadataEntryExtension;
import com.google.api.gbase.client.AttributeHistogram.UniqueValue;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
import java.net.URL;
/**
* This class demonstrates how to retrieve Google Base metadata
* using the client library of the Google Base data API.
*
* The tool implemented by this class will connect to Google Base,
* run the request and display some results.
*/
public class MetadataExample extends Example {
/**
* Runs the example.
*/
public static void main(String[] args) throws IOException, ServiceException {
String queryString = null;
args = init(args, "Google-MetadataExample-1.0");
// Process command-line arguments
if (args.length == 1) {
queryString = args[0];
} else {
System.err.println("Invalid argument count.");
System.err.println("Expected one argument:");
System.err.println(" query");
System.exit(1);
}
queryMetadata(queryString);
}
/**
* Retrieves and prints the list of the most used attributes used by
* the items that match a query.
*
* @param queryString a Google Base Query Language query
*/
private static void queryMetadata(String queryString)
throws IOException, ServiceException {
// Create a query URL from the given arguments
URL url = urlFactory.getAttributesFeedURL();
GoogleBaseQuery query = new GoogleBaseQuery(url);
query.setGoogleBaseQuery(queryString);
// Display the URL generated by the API
System.out.println("Sending request to: " + query.getUrl());
try {
GoogleBaseFeed feed = service.query(query);
// Print the items
printMetadataFeed(feed);
} catch (ServiceException e) {
printServiceException(e);
}
}
/**
* Prints each metadata item in the feed to the output.
* Uses {@link #printMetadataEntry(GoogleBaseEntry)}.
*
* @param feed a Google Base data API metadata feed
*/
private static void printMetadataFeed(GoogleBaseFeed feed) {
if (feed.getTotalResults() == 0) {
System.out.println("No matches.");
return;
}
for (GoogleBaseEntry entry : feed.getEntries()) {
printMetadataEntry(entry);
}
}
/**
* Prints a few relevant attributes and the values of the attribute histogram
* of a metadata GoogleBaseEntry item.
*
* @param entry a Google Base data API metadata entry
*/
private static void printMetadataEntry(GoogleBaseEntry entry) {
MetadataEntryExtension metadata = entry.getGoogleBaseMetadata();
AttributeHistogram attributeHistogram = metadata.getAttributeHistogram();
System.out.println(attributeHistogram.getAttributeName() +
" (" + attributeHistogram.getAttributeType().getName() + "): " +
"valueCount=" + attributeHistogram.getTotalValueCount() + " - " +
entry.getId());
for (UniqueValue value : attributeHistogram.getValues()) {
System.out.println(value.getValueAsString() +
" count=" + value.getCount());
}
}
}
@@ -0,0 +1,49 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.api.gbase.client.GoogleBaseQuery;
import com.google.gdata.client.Service;
/**
* Runs a query on Google Base items.
* This command is called from {@link CustomerTool}.
*/
class QueryCommand extends Command {
private String query;
public void execute() throws Exception {
// Build the query URL
GoogleBaseQuery queryObject = new GoogleBaseQuery(getCustomerFeedURL());
queryObject.setGoogleBaseQuery(query);
Service service = createService();
Service.GDataRequest request =
service.createFeedRequest(queryObject.getUrl());
// Send the request (HTTP GET)
request.execute();
outputRawResponse(request);
}
/** Sets the Google Base query to run. */
public void setQuery(String query) {
this.query = query;
}
}
@@ -0,0 +1,93 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.api.gbase.client.GoogleBaseEntry;
import com.google.api.gbase.client.GoogleBaseFeed;
import com.google.api.gbase.client.GoogleBaseQuery;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
/**
* This class demonstrates how to send simple Google Base search queries
* using the client library of the Google Base data API.
*
* The tool implemented by this class will connect to Google Base, run the
* query and display some results.
*/
public class QueryExample extends Example {
/**
* Maximum number of results to return.
*/
private static final int MAX_RESULTS = 10;
/**
* Runs the example.
*/
public static void main(String[] args) throws IOException, ServiceException {
String queryString = null;
args = init(args, "Google-QueryExample-1.0");
// Process command-line arguments
if (args.length == 1) {
queryString = args[0];
} else {
System.err.println("Invalid argument count.");
System.err.println("Expected one argument:");
System.err.println(" query");
System.exit(1);
}
// Create a query URL from the given arguments
GoogleBaseQuery query =
new GoogleBaseQuery(urlFactory.getSnippetsFeedURL());
query.setGoogleBaseQuery(queryString);
query.setResultFormat(GoogleBaseQuery.ResultFormat.ATOM);
query.setMaxResults(MAX_RESULTS);
// Display the URL generated by the API
System.out.println("Sending request to: " + query.getUrl());
try {
GoogleBaseFeed feed = service.query(query, GoogleBaseFeed.class);
// Print the items
printResult(feed);
} catch (ServiceException e) {
printServiceException(e);
}
}
/**
* Prints a few relevant attributes from each item in the feed to the output.
*
* @param feed a Google Base data API items feed
*/
private static void printResult(GoogleBaseFeed feed) {
if (feed.getTotalResults() == 0) {
System.out.println("No matches.");
} else {
for (GoogleBaseEntry entry : feed.getEntries()) {
System.out.println(entry.getGoogleBaseAttributes().getItemType() +
": " + entry.getTitle().getPlainText() +
" - " +entry.getId());
}
}
}
}
@@ -0,0 +1,46 @@
/* Copyright (c) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
import java.net.URL;
/**
* Queries the specified media feed for all the media attachments
* of one item.
* This command is called from {@link CustomerTool}.
*/
class QueryMediaCommand extends Command {
private String itemMediaUrl;
@Override
public void execute() throws Exception {
Service service = createService();
Service.GDataRequest request = service.createFeedRequest(new URL(itemMediaUrl));
// Send the request (HTTP GET)
request.execute();
outputRawResponse(request);
}
/** Set the url of the media feed. */
public void setItemMediaUrl(String itemMediaUrl) {
this.itemMediaUrl = itemMediaUrl;
}
}
@@ -0,0 +1,44 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
/**
* Modifies an existing item in Google Base.
*
* This command is called from
* {@link com.google.commerce.api.client.cmdline.CustomerTool}.
*/
class UpdateCommand extends Command {
private String itemId;
public void execute() throws Exception {
Service service = createService();
Service.GDataRequest request =
service.createUpdateRequest(fixEditUrl(itemId));
inputRawRequest(request);
// Send the request (HTTP POST)
request.execute();
System.out.println("Item updated successfully.");
}
/** Sets the Id of the item, which is also its URL. */
public void setItemId(String itemId) {
this.itemId = itemId;
}
}
@@ -0,0 +1,45 @@
/* Copyright (c) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.cmdline;
import com.google.gdata.client.Service;
/**
* Modifies the media entry (meta-data) describing a media attachment
* of a Google Base item.
* This command is called from {@link CustomerTool}.
*/
class UpdateMediaCommand extends Command {
private String attachmentId;
@Override
public void execute() throws Exception {
Service service = createService();
Service.GDataRequest request = service.createUpdateRequest(fixEditUrl(attachmentId));
inputRawRequest(request);
// Send the request (HTTP POST)
request.execute();
System.out.println("Item attachment updated successfully.");
}
/** Sets the Id of the media attachment to be updated. */
public void setAttachmentId(String attachmentId) {
this.attachmentId = attachmentId;
}
}
+4
View File
@@ -0,0 +1,4 @@
This directory contains some XML files for uploading
sample data to Google Base via the API. For instance,
the customer tool which is part of the Java client
library examples can be used for submitting the data.
@@ -0,0 +1,30 @@
<?xml version='1.0'?>
<entry xmlns='http://www.w3.org/2005/Atom'
xmlns:g='http://base.google.com/ns/1.0'>
<author>
<name>Jane Doe</name>
<email>JaneDoe@gmail.com</email>
</author>
<category scheme='http://base.google.com/categories/itemtypes'
term='recipes'/>
<title type="text">Marie-Louise's chocolate butter</title>
<content type="xhtml">
<b>Ingredients:</b>
<ul>
<li>250 g margerine,</li>
<li>200 g sugar,</li>
<li>2 eggs, and</li>
<li>approx. 8 tsp cacao.</li>
</ul>
<p>
Mix everything. Heat while stirring, but do not allow the mix to boil.
Put in a container and cool in fridge.
</p>
</content>
<g:item_type type="text">recipes</g:item_type>
<g:cuisine type="text">danish</g:cuisine>
<g:label type="text">butter</g:label>
<g:label type="text">chocolate</g:label>
<g:cooking_time type="intUnit">10 minutes</g:cooking_time>
<g:main_ingredient type="text">cacao</g:main_ingredient>
</entry>
@@ -0,0 +1,36 @@
<?xml version='1.0'?>
<entry xmlns='http://www.w3.org/2005/Atom'
xmlns:g='http://base.google.com/ns/1.0'>
<author>
<name>Jane Doe</name>
<email>JaneDoe@gmail.com</email>
</author>
<category scheme='http://base.google.com/categories/itemtypes'
term='recipes'/>
<title>Ulla's famous chocolate mousse</title>
<content type="xhtml">
<b>Ingredients:</b>
<ul>
<li>125 g dark chocolate,</li>
<li>50 g butter,</li>
<li>50 g sugar, and</li>
<li>4 eggs</li>
</ul>
<p>
Melt the chocolate togther with butter and two tablespoons
of water. Add the sugar. Beat in the egg yolks one at the time.
Add strong coffee, rum, cognac or something similar until the
consistency is like mayonnaise. Beat the egg whites until they are stiff
and gently fold them into the chocolate mix. Poor the mix into a bowl
or individual serving pots and leave in the fridge to cool down.
Serve decorated with whipped cream.
</p>
</content>
<g:item_type type="text">recipes</g:item_type>
<g:cuisine type="text">danish</g:cuisine>
<g:cooking_time type="intUnit">20 minutes</g:cooking_time>
<g:servings type="int">4</g:servings>
<g:course type="text">dessert</g:course>
<g:main_ingredient type="text">dark chocolate</g:main_ingredient>
<g:main_ingredient type="text">butter</g:main_ingredient>
</entry>
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,348 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import com.google.api.gbase.client.FeedURLFactory;
import com.google.api.gbase.client.GoogleBaseService;
import com.google.gdata.client.http.AuthSubUtil;
import com.google.gdata.util.AuthenticationException;
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.security.GeneralSecurityException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Provides an authenticated GoogleBaseService
* to the servlet that will process the request.
*/
public class AuthenticationFilter implements Filter {
/**
* The request attribute that contains the authenticated service.
*/
static final String SERVICE_ATTRIBUTE = "googleBaseService";
/**
* The parameter used by AuthSub to specify the authentication token.
* Also used as the name of the http session attribute that contains
* the session token.
*/
static final String TOKEN_PARAMETER = "token";
static final String TOKEN_COOKIE_NAME = "AuthSubSessionToken";
static final String DEFAULT_AUTHSUB_PROTOCOL = "https";
static final String DEFAULT_AUTHSUB_HOSTNAME = "www.google.com";
protected String authsubProtocol;
protected String authsubHostname;
protected FeedURLFactory urlFactory;
protected String applicationName;
/**
* Developer key, used for identification against the Google Base data
* API servers.
*/
protected String key;
private ServletContext servletContext;
public void init(FilterConfig filterConfig) throws ServletException {
servletContext = filterConfig.getServletContext();
key = servletContext.getInitParameter(RecipeUtil.DEVELOPER_KEY_PARAMETER);
if (key == null || "".equals(key.trim())) {
String errorMessage = "No developer key specified.\n Please edit " +
"web.xml and add your developer key in the \"key\" context " +
"parameter. \n You can obtain a developer key at: \n\t" +
"http://code.google.com/api/base/signup.html";
System.err.println(errorMessage);
throw new ServletException(errorMessage);
}
urlFactory = (FeedURLFactory)
servletContext.getAttribute(RecipeListener.FEED_URL_FACTORY_ATTRIBUTE);
authsubProtocol = filterConfig.getInitParameter("authsubProtocol");
if (authsubProtocol == null) {
authsubProtocol = DEFAULT_AUTHSUB_PROTOCOL;
}
authsubHostname = filterConfig.getInitParameter("authsubHostname");
if (authsubHostname == null) {
authsubHostname = DEFAULT_AUTHSUB_HOSTNAME;
}
}
public void destroy() {
servletContext = null;
urlFactory = null;
applicationName = null;
authsubProtocol = null;
authsubHostname = null;
}
/**
* Starts or stops an authenticated session, depending on the value
* of the {@value #TOKEN_PARAMETER} parameter, or provides the servlets
* with an authenticated
* {@link com.google.api.gbase.client.GoogleBaseService GoogleBaseService},
* during an authenticated session.
*/
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String oneTimeToken = httpRequest.getParameter(TOKEN_PARAMETER);
String sessionToken = getSessionTokenCookie(httpRequest);
if (oneTimeToken != null) {
if ("".equals(oneTimeToken)) {
// Revoke the session token
if (sessionToken != null) {
stopAuthenticatedSession(httpRequest, httpResponse, sessionToken);
return;
}
} else {
// Convert the token to a session token and keep it
try {
startAuthenticatedSession(httpRequest, httpResponse, oneTimeToken);
return;
} catch (GeneralSecurityException e) {
throw new ServletException(e);
} catch (AuthenticationException e) {
// Log and then continue as if this token was not there.
// (It was probably bookmarked.)
servletContext.log("Invalid one-time token", e);
}
}
}
// Request a new token if we don't have one at this point
if (sessionToken == null) {
redirectToAuthSub(httpRequest, httpResponse);
return;
}
// Create a service that authenticates using the session token
GoogleBaseService service = new GoogleBaseService(
applicationName, key, authsubProtocol, authsubHostname);
service.setAuthSubToken(sessionToken);
// Make the service available to the servlet
httpRequest.setAttribute(SERVICE_ATTRIBUTE, service);
// Execute the servlet
try {
filterChain.doFilter(request, response);
} catch(ServletException e) {
Throwable cause = e.getRootCause();
if (cause instanceof AuthenticationException &&
!response.isCommitted()) {
// Token has been revoked. Re-run AuthSub.
redirectToAuthSub(httpRequest, httpResponse);
} else {
// Let the exception be handled as usual.
throw e;
}
}
}
/**
* Gets the AuthSub session token from a cookie.
*
* @param httpRequest
* @return session token or null
*/
String getSessionTokenCookie(HttpServletRequest httpRequest) {
Cookie[] cookies = httpRequest.getCookies();
if (cookies == null) {
return null;
}
for (Cookie cookie : cookies) {
if (cookie.getName().equals(TOKEN_COOKIE_NAME)) {
return cookie.getValue();
}
}
return null;
}
/**
* Revokes the specified session token and redirects the browser to
* the context of the request.
*
* @param request
* @param response
* @param sessionToken a session token
* @throws IOException
* @throws ServletException
*/
private void stopAuthenticatedSession(HttpServletRequest request,
HttpServletResponse response,
String sessionToken)
throws IOException, ServletException {
if (sessionToken != null) {
revokeSessionToken(sessionToken);
clearSessionTokenCookie(request, response);
}
String url = request.getContextPath();
response.sendRedirect(response.encodeRedirectURL(url));
}
/**
* Force cookie expiration by sending an expired cookie to
* the browser.
*
* @param request
* @param response
*/
private void clearSessionTokenCookie(HttpServletRequest request,
HttpServletResponse response)
throws ServletException {
response.addCookie(newExpiredSessionTokenCookie(request));
}
private Cookie newExpiredSessionTokenCookie(HttpServletRequest request)
throws ServletException {
Cookie cookie = newSessionTokenCookie(request, "");
cookie.setMaxAge(0);
return cookie;
}
/**
* Explicitely revoke the session token.
*
* @param token
* @throws IOException
* @throws ServletException
*/
protected void revokeSessionToken(String token) throws IOException, ServletException
{
try {
AuthSubUtil.revokeToken(authsubProtocol, authsubHostname, token, null);
} catch (AuthenticationException e) {
throw new ServletException(e);
} catch (GeneralSecurityException e) {
throw new ServletException(e);
}
}
/**
* Exchanges the single use token for a session token and
* redirects the browser to the same address with the
* token specification part removed.
*
* @param request
* @param response
* @param oneTimeToken a single use AuthSub token
* @throws IOException
* @throws GeneralSecurityException
* @throws AuthenticationException if the one-time token is invalid
*/
private void startAuthenticatedSession(HttpServletRequest request,
HttpServletResponse response,
String oneTimeToken)
throws IOException, GeneralSecurityException, AuthenticationException,
ServletException {
String sessionToken = exchangeForSessionToken(oneTimeToken);
// Store the authentication token in a cookie
response.addCookie(newSessionTokenCookie(request, sessionToken));
// Redirect the browser to the same address
// with the "token=value" part removed from the query string
StringBuffer url = request.getRequestURL();
String queryString = request.getQueryString();
if (queryString != null) {
queryString = queryString.replaceFirst("token=[^&]*&?", "");
if (queryString.length() > 0) {
url.append("?").append(queryString);
}
}
response.sendRedirect(response.encodeRedirectURL(url.toString()));
}
protected Cookie newSessionTokenCookie(HttpServletRequest request,
String sessionToken)
throws ServletException {
Cookie cookie = new Cookie(TOKEN_COOKIE_NAME, sessionToken);
// AuthSub session tokens effectively don't expire. Hang on to it.
// If the AuthSub token is revoked, the filter will request
// a new token.
cookie.setMaxAge(365*24*60*60);
cookie.setPath(request.getContextPath() + "/");
try {
cookie.setDomain(new URL(request.getRequestURL().toString()).getHost());
} catch(MalformedURLException e) {
throw new ServletException(e);
}
// Cookie domain set automatically by the server.
return cookie;
}
/**
* Converts a one-time token into a reusable session token.
* @param oneTimeToken
* @throws IOException
*/
protected String exchangeForSessionToken(String oneTimeToken)
throws IOException, GeneralSecurityException, AuthenticationException {
return AuthSubUtil.exchangeForSessionToken(authsubProtocol,
authsubHostname,
oneTimeToken,
null);
}
/**
* Redirects to the AuthSub authentication page.
*
* @param request
* @param response
* @throws IOException
*/
private void redirectToAuthSub(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
StringBuffer next = request.getRequestURL();
String queryString = request.getQueryString();
if (queryString != null && !"".equals(queryString) ) {
next.append("?").append(queryString);
}
String scope = new URL(urlFactory.getBaseURL(), "feeds").toExternalForm();
String url = AuthSubUtil.getRequestUrl(authsubProtocol,
authsubHostname,
next.toString(),
scope,
false,
true);
response.sendRedirect(response.encodeRedirectURL(url));
}
}
@@ -0,0 +1,254 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/**
* Methods called from the JSP for generating HTML code.
*
* This is the work normally done by a web framework.
*/
public class DisplayUtils {
/**
* Prints checkboxes on two colums, with a text input field at the end.
* The checked values will be displayed along with the values to be showed.
*
* The text input field can be used to input a custom value. It has the
* same name as the checkbox input fields, so when the submitted values
* are processed, you have to remove the empty value, in case this field
* remains empty.
*
* @param out output writer
* @param name name of the input field
* @param values values to be shown
* @param checked checked values
* @throws IOException
*/
public static void printCheckboxes(Writer out,
String name,
String[] values,
Set<String> checked) throws IOException {
Set<String> allValues = new TreeSet<String>();
// The displayed values are the static values plus the checked values
allValues.addAll(Arrays.asList(values));
if (checked == null) {
checked = new HashSet<String>();
}
allValues.addAll(checked);
int middle = (allValues.size() + 2) / 2;
int row = 0;
out.write("<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">");
out.write("<tr><td><ul class=\"inputlist\">");
for (String value : allValues) {
if (row == middle) {
out.write("</ul></td><td><ul class=\"inputlist\">");
}
out.write("<li>");
printCheckbox(out, name, value, checked.contains(value));
out.write("</li>");
row++;
}
out.write("<li><label>Other:<br/><input id=\"other");
out.write(name);
out.write("\" type=\"text\" name=\"");
out.write(name);
out.write("\" value=\"\" size=\"15\" class=\"txt\"></label></li>");
out.write("</ul></td></tr></table>");
}
/**
* Prints a checkbox input field with a label.
*
* @param out output writer
* @param name name of the field
* @param value value of the field
* @param checked true if the field is checked
* @throws IOException
*/
public static void printCheckbox(Writer out, String name, String value,
boolean checked) throws IOException {
value = escape(value);
out.write("<label><input type=\"checkbox\" name=\"");
out.write(name);
out.write("\" value=\"");
out.write(value);
out.write("\"");
if (checked) {
out.write(" checked");
}
out.write("/>&nbsp;");
if (checked) {
out.write("<b>");
}
out.write(value);
if (checked) {
out.write("</b>");
}
out.write("</label>");
}
/**
* Escapes the items of the Collection and returns them in a StringBuffer,
* separated by commas.
*
* @param list
* @return list of the elements of the collection, sepparated by commas
*/
public static StringBuffer printList(Collection<String> list) {
StringBuffer sb = new StringBuffer();
if (list != null && list.size() > 0) {
Iterator<String> iter = list.iterator();
while (true) {
sb.append(escape(iter.next()));
if (iter.hasNext()) {
sb.append(", ");
} else {
break;
}
}
}
return sb;
}
/**
* Prints &lt;option&gt; tags and set the selected option.
*
* @param out output writer
* @param names option names, which are used both for
* values and for labels
* @param current name of the current option
* @throws IOException
*/
public static void printOptions(Writer out,
String[] names,
String current) throws IOException {
for (int i=0; i<names.length; i++) {
String name = names[i];
printOption(out, name, name, name.equals(current));
}
printOption(out, "all", "", current==null);
}
/**
* Prints one &lt;option&gt; tag and select it if necessary.
*
* @param out output writer
* @param label option label
* @param value option value
* @param selected true if it's selected
* @throws IOException
*/
public static void printOption(Writer out,
String label,
String value,
boolean selected) throws IOException {
out.write("<option");
if (selected) {
out.write(" selected");
}
out.write(" value=\"");
out.write(value);
out.write("\">");
out.write(escape(label));
out.write("</option>");
}
/**
* Simple HTML escaping of the String version of the specified Object.
*
* @param obj Object that has a meaningful toString() value
* @return string with some escaped characters
*/
public static String escape(Object obj) {
if (obj == null) {
return "";
}
return escapeAndShorten(obj.toString(), -1);
}
/**
* Simple HTML escaping.
*
* Escapes &lt; &amp; and &gt; and leaves the rest as it is.
*
* @param raw
* @return string with some escaped characters
*/
public static String escape(String raw) {
return escapeAndShorten(raw, -1);
}
/**
* Escape HTML data and shorted the result if necessary.
*
* If the text is escaped, &lt;b&gt;...&lt;/b&gt; will be
* appended.
*
* @param raw raw text to escape
* @param maxLength maximum output length
* @return HTML code
*/
public static String escapeAndShorten(String raw, int maxLength) {
if (raw == null) {
return "";
}
StringBuilder retval = new StringBuilder();
int length = raw.length();
boolean shortened = false;
if (maxLength != -1 && length > maxLength) {
length = maxLength;
shortened = true;
}
for (int i=0; i<length; i++) {
char c = raw.charAt(i);
switch (c) {
case '<':
retval.append("&lt;");
break;
case '>':
retval.append("&gt;");
break;
case '&':
retval.append("&amp;");
break;
case '\'':
retval.append("&#039;");
break;
case '"':
retval.append("&#034;");
break;
default:
retval.append(c);
break;
}
}
if (shortened) {
retval.append("<b>...</b>");
}
return retval.toString();
}
}
@@ -0,0 +1,318 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import com.google.api.gbase.client.AttributeHistogram;
import com.google.api.gbase.client.FeedURLFactory;
import com.google.api.gbase.client.GoogleBaseEntry;
import com.google.api.gbase.client.GoogleBaseFeed;
import com.google.api.gbase.client.GoogleBaseQuery;
import com.google.api.gbase.client.GoogleBaseService;
import com.google.api.gbase.client.MetadataEntryExtension;
import com.google.api.gbase.client.AttributeHistogram.UniqueValue;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import javax.servlet.ServletContext;
/**
* Holds the most used values of attributes in Google Base and
* periodically refreshes them.
*
* A MostUsedValues object is focused on a GoogleBaseService and
* a FeedURLFactory. One object can be used to analyse only the items that
* match a specific query.
*
* The class currently works for TEXT attributes only.
*
* The cache() and clear() calls are synchronized,
* so that the different Maps and Collections remain in sync.
*
* Typically, right after you create a MostUsedValues object, you call the
* {@link #cache(long, int, ServletContext, String[])} method to set it up
* to cache the most used values of your attributes. This creates
* {@link java.util.TimerTask} objects that periodically refresh the cached
* values. After this, you use {@link #getMostUsedValuesForAttribute(String)}
* to get the most used values for the attributes you previously specified.
* In the end, when you no longer need the cache, you can call
* {@link #clear()} to stop the TimerTask objects and clear the cache.
*/
public class MostUsedValues {
protected static final String TEXT_TYPE = "(text)";
/**
* The initial value of the max values limit when making a query;
* the step used to increase that limit if some of the attributes
* are not found.
*/
protected static final int STEP_MAXRESULTS = 25;
/**
* The max of the max values limit when making a query.
*/
protected static final int MAX_MAXRESULTS = 200;
/**
* The cached most used values.
* This is a synchronized map.
*/
private java.util.Map<String, String[]> mostUsedValues;
/**
* The timers that periodically refresh the cache.
* The access to this object has to be synchronized.
*/
private Collection<Timer> timers;
private GoogleBaseService service;
private FeedURLFactory urlFactory;
private String queryString;
/**
* Creates an empty MostUsedValues.
*
* @param service any GoogleBaseService used to retrieve attribute histograms
* @param urlFactory a FeedURLFactory used to create the URLs of
* the attribute histograms
* @param queryString the query string used to filter the analyzed items,
* for example one that focuses only on the items that
* have a specific item type.
*/
public MostUsedValues(GoogleBaseService service,
FeedURLFactory urlFactory,
String queryString) {
this.service = service;
this.urlFactory = urlFactory;
this.queryString = queryString;
mostUsedValues = new Hashtable<String, String[]>();
timers = new ArrayList<Timer>();
}
/**
* Gets the cached most used values of an attribute.
*
* @param attrName the name of the attribute
* @return a cached list of the most used values
*/
public String[] getMostUsedValuesForAttribute(String attrName) {
return mostUsedValues.get(attrName);
}
/**
* Sets up the object to cache a limited number of the most used values of
* each of the specified attributes; the cache is refreshed periodically.
*
* This method does not check if the specified attributes are already cached.
* You have to make sure an attribute is specified only once in your calls.
*
* @param interval the cache refresh period, in millis
* @param maxValues the maximum number of values to cache
* @param servletContext a ServletContext to be used for logging
* @param attrNames the names of the attributes to be cached
*/
synchronized public void cache(long interval,
final int maxValues,
ServletContext servletContext,
final String... attrNames) {
if (attrNames.length == 0) {
return;
}
Timer timer = new Timer(true);
TimerTask task = createRefresher(maxValues, servletContext, attrNames);
task.run();
timer.schedule(task, interval, interval);
timers.add(timer);
}
/**
* Creates a TimerTask that refreshes the cached most used values of
* the specified attributes.
*
* @param maxValues how many values to cache for each attribute
* @param servletContext servlet context for logging error messages
* @param attrNames the names of the attributes
* @return a TimerTask that refreshes the cache
*/
private TimerTask createRefresher(final int maxValues,
final ServletContext servletContext,
final String... attrNames) {
TimerTask task = new TimerTask() {
/**
* Tells the MostUsedValues object that created this TimerTask to
* refresh the cached most used values of some of the attributes.
*/
@Override public void run() {
try {
MostUsedValues.this.retrieveMostUsedValues(maxValues, attrNames);
} catch (IOException e) {
servletContext.log(e.getMessage(), e);
} catch (ServiceException e) {
servletContext.log(e.getMessage() + " " +
e.getHttpErrorCodeOverride() + " " +
e.getResponseContentType() + ": " +
e.getResponseBody(), e);
}
}
};
return task;
}
/**
* Retrieves the most used values for some attributes
* for the items that match the query string
* and stores a limited number of those values for each attribute.
*
* @param numValue maximum number of values to store
* @param attrNames the names of the attributes
*/
protected void retrieveMostUsedValues(int numValue, final String... attrNames)
throws ServiceException, IOException {
URL url = urlFactory.getAttributesFeedURL();
GoogleBaseQuery query = new GoogleBaseQuery(url);
StringBuffer queryString = createQueryString(attrNames);
query.setGoogleBaseQuery(queryString.toString());
query.setMaxValues(numValue);
int numResults = 0;
int lastNumResults = 0;
Collection<String> attrToRetrieve =
new ArrayList<String>(Arrays.asList(attrNames));
do {
// Get the feed
numResults += STEP_MAXRESULTS;
query.setMaxResults(numResults);
GoogleBaseFeed feed = service.query(query);
if (lastNumResults == feed.getTotalResults()) {
// No new entries to process
break;
}
lastNumResults = feed.getTotalResults();
// Extract the values from the entries
Iterator<String> attrIter = attrToRetrieve.iterator();
while (attrIter.hasNext()) {
String attrName = attrIter.next();
// Searching for the entry with the following name
String entryTitle = attrName + TEXT_TYPE;
for (GoogleBaseEntry entry : feed.getEntries()) {
if (entryTitle.equals(entry.getTitle().getPlainText())) {
extractValuesFromEntry(numValue, attrName, entry);
attrIter.remove();
}
}
}
} while (!attrToRetrieve.isEmpty() && numResults <= MAX_MAXRESULTS);
if (!attrToRetrieve.isEmpty()) {
throw new ServiceException("The retrieved histograms do not contain" +
"some of the attributes. The most used values of these attributes " +
"have not been refreshed.");
}
}
/**
* Returns the query string extended so that it filters out the items
* that do not have at least one of the specified attributes of type TEXT.
*
* @param attrNames the attributes we are interested in
* @return an extended query string
*/
protected StringBuffer createQueryString(final String... attrNames) {
StringBuffer queryString = new StringBuffer(this.queryString);
queryString.append("(");
queryString.append("[").append(attrNames[0]).append(TEXT_TYPE).append("]");
for (int i = 1; i < attrNames.length; i++) {
String attrName = attrNames[i];
queryString.append("|[").append(attrName).append(TEXT_TYPE).append("]");
}
queryString.append(")");
return queryString;
}
/**
* Caches a limited number of the values of a GoogleBaseEntry.
*
* @param numValue maximum number of values to cache
* @param attrName the name of the attribute that has the values
* @param entry an entry with a MetadataEntryExtension
*/
private void extractValuesFromEntry(int numValue,
String attrName,
GoogleBaseEntry entry) {
MetadataEntryExtension metadata = entry.getGoogleBaseMetadata();
AttributeHistogram attributeHistogram = metadata.getAttributeHistogram();
List<? extends UniqueValue> values = attributeHistogram.getValues();
int valuesCount = Math.min(numValue, values.size());
String[] usedValues = new String[valuesCount];
for (int i = 0; i < valuesCount; i++) {
usedValues[i] = values.get(i).getValueAsString();
}
updateMostUsedValue(attrName, usedValues);
}
/**
* Cancels all refresh timers and clears the cache.
*/
synchronized public void clear() {
for (Timer timer : timers) {
timer.cancel();
}
timers.clear();
mostUsedValues.clear();
}
/**
* Returns the number of cached attributes.
*/
public int size() {
return mostUsedValues.size();
}
/**
* Returns true if no attribute is cached.
*/
public boolean isEmpty() {
return mostUsedValues.isEmpty();
}
public String getQueryString() {
return queryString;
}
/**
* Caches the most used values of an attribute.
* @param attrName
* @param stringValues
*/
protected void updateMostUsedValue(String attrName, String[] stringValues) {
mostUsedValues.put(attrName, stringValues);
}
}
+338
View File
@@ -0,0 +1,338 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import com.google.api.gbase.client.GoogleBaseEntry;
import com.google.api.gbase.client.NumberUnit;
import com.google.gdata.data.Content;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.OtherContent;
import com.google.gdata.data.Person;
import com.google.gdata.data.TextConstruct;
import com.google.gdata.data.TextContent;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* One recipe, ready to be displayed.
* Just a data holding object.
*/
public class Recipe {
public final static String RECIPE_ITEMTYPE = "recipes";
public final static String MAIN_INGREDIENT_ATTRIBUTE = "main ingredient";
public final static String CUISINE_ATTRIBUTE = "cuisine";
public final static String COOKING_TIME_ATTRIBUTE = "cooking time";
public final static String AUTHOR_UNKNOWN = "";
private final String id;
private final DateTime postedOn;
private final String postedBy;
private final NumberUnit<Integer> cookingTime;
private final String url;
private final String title;
private final String description;
/** A never-null list that contains the main ingredients. */
private final Set<String> mainIngredient;
/** A never-null list that contains the cuisines. */
private final Set<String> cuisine;
/**
* Creates a recipe. The parameters can be null.
*
* @param id id generated by the GoogleBase server
* @param title
* @param url alternate url of the recipe;
* if null, when the recipe is used to insert or to update
* it is generated by the GoogleBase server
* @param description
* @param mainIngredient
* @param cuisine
* @param cookingTime
*/
public Recipe(String id, String title, String url,
String description, Set<String> mainIngredient, Set<String> cuisine,
NumberUnit<Integer> cookingTime) {
if (mainIngredient == null) {
mainIngredient = new HashSet<String>();
}
if (cuisine == null) {
cuisine = new HashSet<String>();
}
this.id = id;
this.title = title;
this.url = url;
this.description = description;
this.mainIngredient = mainIngredient;
this.cuisine = cuisine;
this.cookingTime = cookingTime;
this.postedOn = null;
this.postedBy = null;
}
/**
* Creates a recipe out of a GoogleBaseEntry.
*
* @param entry an entry that represents a recipe
*/
public Recipe(GoogleBaseEntry entry) {
id = extractIdFromUrl(entry.getId());
title = entry.getTitle().getPlainText();
url = entry.getHtmlLink().getHref();
String description = null;
if (entry.getContent() != null) {
Content content = entry.getContent();
if (content instanceof TextContent) {
description = ((TextContent)content).getContent().getPlainText();
} else if (content instanceof OtherContent) {
description = ((OtherContent)content).getText();
}
}
this.description = description;
mainIngredient = new HashSet<String>(entry.getGoogleBaseAttributes().
getTextAttributeValues(MAIN_INGREDIENT_ATTRIBUTE));
cuisine = new HashSet<String>(entry.getGoogleBaseAttributes().
getTextAttributeValues(CUISINE_ATTRIBUTE));
cookingTime = entry.getGoogleBaseAttributes().
getIntUnitAttribute(COOKING_TIME_ATTRIBUTE);
postedOn = entry.getPublished();
// if an entry has no author specified, will set it to empty string
List<Person> authors = entry.getAuthors();
postedBy = (authors.isEmpty() ? AUTHOR_UNKNOWN : authors.get(0).getName());
}
/**
* Creates an empty recipe, the values are null or empty Sets.
*/
public Recipe() {
this(null, null, null, null, null, null, null);
}
public GoogleBaseEntry toGoogleBaseEntry(String idUrl)
{
GoogleBaseEntry entry = new GoogleBaseEntry();
entry.getGoogleBaseAttributes().setItemType(RECIPE_ITEMTYPE);
if (idUrl != null) {
entry.setId(idUrl);
}
entry.setTitle(TextConstruct.create(TextConstruct.Type.TEXT, title, null));
if (url != null) {
entry.addHtmlLink(url, null, null);
}
if (description != null) {
// If the original content was not TEXT, the formatting is lost
entry.setContent(
TextConstruct.create(TextConstruct.Type.TEXT, description, null));
}
for (String ingredient : mainIngredient) {
entry.getGoogleBaseAttributes().addTextAttribute(
MAIN_INGREDIENT_ATTRIBUTE, ingredient);
}
for (String cuisineItem : cuisine) {
entry.getGoogleBaseAttributes().addTextAttribute(
CUISINE_ATTRIBUTE, cuisineItem);
}
if (cookingTime != null) {
entry.getGoogleBaseAttributes().addIntUnitAttribute(
COOKING_TIME_ATTRIBUTE, cookingTime);
}
return entry;
}
/**
* Extracts the id of the item from the given url.
*
* The id found in GoogleBaseEntry is a URL that contains the real, numerical
* id of the item in Google Base. Parsing the URL is unfortunately the
* only way of getting a numerical id given a GoogleBaseEntry.
*
* @param url a URL that ends with "/" [N] number
*/
private static String extractIdFromUrl(String url) {
int lastSlash = url.lastIndexOf('/');
if (lastSlash == -1 || lastSlash == (url.length()-1)) {
throw new IllegalArgumentException("Id is in a strange format. " + url);
}
String oid = url.substring(lastSlash + 1);
return oid;
}
/** Checks whether there is a description for the recipe. */
public boolean hasDescription() {
return description != null;
}
/** Checks whether there is a cooking time for the recipe. */
public boolean hasCookingTime() {
return cookingTime != null;
}
/** Checks whether there are some cuisines for the recipe. */
public boolean hasCuisine() {
return cuisine.size() > 0;
}
/** Checks whether there are some main ingredients for the recipe. */
public boolean hasMainIngredient() {
return mainIngredient.size() > 0;
}
/**
* Gets the date at which the recipe was posted, as a string.
*
* @param detailed set to true to get a full date and time
*/
public String getPostedOnAsString(boolean detailed) {
Date date = new Date(postedOn.getValue());
String template = detailed ? "MMMMM d, yyyy HH:mm z" : "MMM d";
DateFormat format = new SimpleDateFormat(template);
return format.format(date);
}
/** Gets the host and protocol from the recipe URL. */
public String getHostAndProtocol() throws MalformedURLException {
URL urlObject = new URL(getUrl());
return urlObject.getProtocol() + "://" + urlObject.getHost();
}
/**
* Returns true when the title, description, mainIngredient and cuisine
* attributes are not null nor empty.
*/
public boolean isComplete() {
return title != null &&
description != null &&
mainIngredient.size() > 0 &&
cuisine.size() > 0;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Recipe[");
appendNamedParameter(sb, "title", title);
appendNamedParameter(sb, "id", id);
appendNamedParameter(sb, "url", url);
appendNamedParameter(sb, "description", description);
appendNamedParameter(sb, "cookingTime", cookingTime);
appendNamedParameter(sb, "postedOn", postedOn);
appendNamedParameter(sb, "postedBy", postedBy);
appendNamedCollection(sb, "mainIngredient", mainIngredient);
appendNamedCollection(sb, "cuisine", cuisine);
sb.append("]");
return sb.toString();
}
/**
* Appends the name and the value of an Object to a StringBuffer.
*
* @param sb
* @param name
* @param value
*/
private static void appendNamedParameter(StringBuffer sb,
String name,
Object value) {
if (value != null) {
sb.append(name).append("=\"").append(value).append("\" ");
}
}
/**
* Appends the name and the elements of a Collection to a StringBuffer.
*
* @param sb
* @param name
* @param collection
*/
private static void appendNamedCollection(
StringBuffer sb,
String name,
Collection<String> collection) {
if (collection.size() > 0) {
sb.append(name).append("=(");
for (String value : collection) {
sb.append("\"").append(value).append("\", ");
}
sb.append(") ");
}
}
/** Gets the id generated by the server. */
public String getId() {
return id;
}
/** Gets the user assigned title. */
public String getTitle() {
return title;
}
/**
* Gets the url of the recipe, as set by the customer.
* If the customer sets no url, the server will generate one.
*/
public String getUrl() {
return url;
}
/** Gets the user assigned description. */
public String getDescription() {
return description;
}
/** Returns a never-null Set with the main ingredients of the recipe. */
public Set<String> getMainIngredient() {
return mainIngredient;
}
/** Returns a never-null Set with the cuisines the recipe belongs to. */
public Set<String> getCuisine() {
return cuisine;
}
/** Gets the user assigned cooking time. */
public NumberUnit<Integer> getCookingTime() {
return cookingTime;
}
/** Gets the server generated owner attribute of the recipe. */
public String getPostedBy() {
return postedBy;
}
/** Gets the server generated date at which the recipe was posted. */
public DateTime getPostedOn() {
return postedOn;
}
/** Returns true when the Recipe doesn't have an id. */
public boolean isNew() {
return id == null;
}
}
@@ -0,0 +1,265 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import com.google.api.gbase.client.FeedURLFactory;
import com.google.api.gbase.client.GoogleBaseEntry;
import com.google.api.gbase.client.GoogleBaseService;
import com.google.api.gbase.client.NumberUnit;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
import java.net.URL;
import java.util.Set;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Inserts, updates or deletes a recipe,
* depending on the "action" servlet initialization parameter.
*/
@SuppressWarnings("serial")
public class RecipeActionServlet extends HttpServlet {
public static final String DISPLAY_JSP = "/WEB-INF/recipeEdit.jsp";
private static final int ACTION_ADD = 0;
private static final int ACTION_UPDATE = 1;
private static final int ACTION_DELETE = 2;
protected FeedURLFactory urlFactory;
/** The operation this servlet has to perform. */
protected int action = -1;
@Override
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
ServletContext context = servletConfig.getServletContext();
urlFactory = (FeedURLFactory)
context.getAttribute(RecipeListener.FEED_URL_FACTORY_ATTRIBUTE);
String action = servletConfig.getInitParameter("action");
if ("add".equals(action)) {
this.action = ACTION_ADD;
} else if ("update".equals(action)) {
this.action = ACTION_UPDATE;
} else if ("delete".equals(action)) {
this.action = ACTION_DELETE;
} else {
throw new ServletException("Unknown action: " + action);
}
}
private boolean isAdd() { return ACTION_ADD == action; }
private boolean isUpdate() { return ACTION_UPDATE == action; }
private boolean isDelete() { return ACTION_DELETE == action; }
@Override
public void destroy() {
super.destroy();
}
/** Inserts or updates the submitted recipe and redirects to recipeList. */
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
GoogleBaseService service = (GoogleBaseService) request.getAttribute(
AuthenticationFilter.SERVICE_ATTRIBUTE);
Recipe recipe = getPostedRecipe(request);
if (!recipe.isComplete()) {
String message = "<div class='errormessage'>Please fill out " +
"all the mandatory fields.</div>";
editRecipe(request, response, recipe, message);
} else {
try {
if (isAdd()) {
recipeAdd(service, recipe);
} else if (isUpdate()) {
recipeUpdate(service, recipe);
} else {
throw new ServletException("Unknown POST action: " + action);
}
} catch (ServiceException e) {
RecipeUtil.logServiceException(this, e);
RecipeUtil.forwardToErrorPage(request, response, e);
return;
}
listOwnRecipes(response);
}
}
/** Redirect to the page that lists customer's recipes. */
protected void listOwnRecipes(HttpServletResponse response)
throws IOException {
String redirectUrl = "recipeList";
response.sendRedirect(response.encodeRedirectURL(redirectUrl));
}
/**
* Inserts a recipe using the specified authenticated service.
*
* @param service an authenticated GoogleBaseService
* @param recipe recipe to be inserted
* @throws IOException
* @throws ServiceException
*/
protected void recipeAdd(GoogleBaseService service,
Recipe recipe)
throws IOException, ServiceException {
URL feedUrl = urlFactory.getItemsFeedURL();
GoogleBaseEntry entry = recipe.toGoogleBaseEntry(null);
service.insert(feedUrl, entry);
}
/**
* Updates a recipe using the specified authenticated service.
*
* The recipe must have a valid GoogleBase id.
*
* @param service an authenticted GoogleBaseService
* @param recipe recipe to be updated
* @throws ServiceException
* @throws IOException
*/
protected void recipeUpdate(GoogleBaseService service,
Recipe recipe)
throws ServiceException, IOException {
URL feedUrl = urlFactory.getItemsEntryURL(recipe.getId());
GoogleBaseEntry entry = recipe.toGoogleBaseEntry(feedUrl.toString());
service.update(feedUrl, entry);
}
/**
* Uses the specified authenticated service to delete a recipe.
*
* @param service an authenticated service
* @param id the id of the recipe
* @throws ServiceException
* @throws IOException
*/
protected void recipeDelete(GoogleBaseService service,
String id)
throws ServiceException, IOException {
URL feedUrl = urlFactory.getItemsEntryURL(id);
service.delete(feedUrl);
}
/**
* Builds a Recipe from the parameters submitted with the specified request.
*
* @param request http request to be processed
* @return a submitted recipe
*/
static Recipe getPostedRecipe(HttpServletRequest request) {
String id = getNullIfEmpty(request.getParameter(RecipeUtil.ID_PARAMETER));
String title = getNullIfEmpty(request.getParameter(
RecipeUtil.TITLE_PARAMETER));
String url = getNullIfEmpty(request.getParameter(RecipeUtil.URL_PARAMETER));
String description = getNullIfEmpty(request.getParameter(
RecipeUtil.DESCRIPTION_PARAMETER));
Set<String> mainIngredient = RecipeUtil.validateValues(
request.getParameterValues(RecipeUtil.MAIN_INGREDIENT_PARAMETER));
Set<String> cuisine = RecipeUtil.validateValues(
request.getParameterValues(RecipeUtil.CUISINE_PARAMETER));
NumberUnit<Integer> cookingTime;
try {
cookingTime = new NumberUnit<Integer>(
new Integer(request.getParameter(RecipeUtil.COOKING_TIME_PARAMETER)),
RecipeUtil.COOKING_TIME_UNIT);
} catch (Exception e) {
// If anything goes bad, we set cookingTime to null
cookingTime = null;
}
Recipe recipe = new Recipe(id,
title,
url,
description,
mainIngredient,
cuisine,
cookingTime);
return recipe;
}
static private String getNullIfEmpty(String s) {
return s != null && "".equals(s) ? null : s;
}
/** Shows the page for inserting or updating a recipe or deletes a recipe. */
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
GoogleBaseService service = (GoogleBaseService) request.getAttribute(
AuthenticationFilter.SERVICE_ATTRIBUTE);
String id = request.getParameter(RecipeUtil.ID_PARAMETER);
try {
if (isDelete()) {
recipeDelete(service, id);
listOwnRecipes(response);
} else {
// The recipe that will be used on the edit page
// for inserting or updating.
Recipe recipe = null;
if (isAdd()) {
recipe = new Recipe();
} else if (isUpdate()) {
URL entryUrl = urlFactory.getItemsEntryURL(id);
GoogleBaseEntry entry = service.getEntry(entryUrl);
recipe = new Recipe(entry);
}
if (recipe != null) {
// Ready to add or update
editRecipe(request, response, recipe, null);
}
}
} catch (ServiceException e) {
RecipeUtil.logServiceException(this, e);
RecipeUtil.forwardToErrorPage(request, response, e);
return;
}
}
/**
* Sets the {@value RecipeUtil#RECIPE_ATTRIBUTE} attribute of the request to
* contain the specified recipe and forwards the request to the
* {@value #DISPLAY_JSP} page.
*
* @param request
* @param response
* @param recipe the recipe to be passed to the edit jsp page
* @param message HTML code to be displayed at the top of the page, usually an
* error message
*/
private void editRecipe(HttpServletRequest request,
HttpServletResponse response,
Recipe recipe,
String message)
throws ServletException, IOException {
request.setAttribute(RecipeUtil.RECIPE_ATTRIBUTE, recipe);
request.setAttribute(RecipeUtil.MESSAGE_ATTRIBUTE,
message == null ? "" : message);
// Forward to the JSP
request.getRequestDispatcher(DISPLAY_JSP).forward(request, response);
}
}
@@ -0,0 +1,105 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import com.google.api.gbase.client.FeedURLFactory;
import com.google.api.gbase.client.GoogleBaseEntry;
import com.google.api.gbase.client.GoogleBaseService;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
import java.net.URL;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Displays a recipe.
*/
@SuppressWarnings("serial")
public class RecipeDisplayServlet extends HttpServlet {
public static final String DISPLAY_JSP = "/WEB-INF/recipeDisplay.jsp";
protected FeedURLFactory urlFactory;
@Override
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
ServletContext context = servletConfig.getServletContext();
urlFactory = (FeedURLFactory)
context.getAttribute(RecipeListener.FEED_URL_FACTORY_ATTRIBUTE);
}
@Override
public void destroy() {
super.destroy();
}
/**
* Shows the page for displaying a Recipe.
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// This is a public page, so we use a simple, nonauthenticated service
GoogleBaseService service = RecipeUtil.getGoogleBaseService(request,
this.getServletContext());
String id = request.getParameter(RecipeUtil.ID_PARAMETER);
recipeDisplay(request, response, service, id);
}
/**
* Retrieves a recipe and forwards the request
* to the {@link #DISPLAY_JSP} jsp page that displays the recipe.
*
* @param request
* @param response
* @param service the service used to retrieve the recipe
* @param id the id of the recipe
*/
private void recipeDisplay(HttpServletRequest request,
HttpServletResponse response,
GoogleBaseService service,
String id)
throws ServletException, IOException {
GoogleBaseEntry entry;
try {
URL feedUrl = urlFactory.getSnippetsEntryURL(id);
entry = service.getEntry(feedUrl, GoogleBaseEntry.class);
} catch (ServiceException e) {
RecipeUtil.logServiceException(this, e);
RecipeUtil.forwardToErrorPage(request, response, e);
return;
}
Recipe recipe = new Recipe(entry);
request.setAttribute(RecipeUtil.RECIPE_ATTRIBUTE, recipe);
RecipeSearch results = new RecipeSearch(service, urlFactory, false);
RecipeUtil.setRecipeSearch(request, results);
// Forward to the JSP
request.getRequestDispatcher(DISPLAY_JSP).forward(request, response);
}
}
@@ -0,0 +1,109 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import com.google.api.gbase.client.FeedURLFactory;
import com.google.api.gbase.client.GoogleBaseService;
import java.net.MalformedURLException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* Creates objects needed by the servlets and makes them available by
* setting them as attributes of the global servlet context.
*
* Makes sure the required initialization parameters are present.
*
*/
public class RecipeListener implements ServletContextListener {
public static final String MOST_USED_VALUES_ATTRIBUTE = "mostUsedValues";
public static final String FEED_URL_FACTORY_ATTRIBUTE = "feedUrlFactory";
FeedURLFactory urlFactory;
protected MostUsedValues mostUsedValues;
/**
* Creates an initialised MostUsedValues object and a FeedURLFactory
* to be used by the servlets.
* Makes sure the applicationName init parameter is set.
*
* @throws RuntimeException
*/
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
String applicationName =
servletContext.getInitParameter(RecipeUtil.APPLICATION_NAME_PARAMETER);
if (applicationName == null) {
RuntimeException re =
new RuntimeException("applicationName context parameter is missing");
servletContext.log(re.getMessage(), re.getCause());
throw re;
}
String baseUrl = servletContext.getInitParameter("baseUrl");
if (baseUrl == null) {
urlFactory = FeedURLFactory.getDefault();
} else {
try {
urlFactory = new FeedURLFactory(baseUrl);
} catch (MalformedURLException e) {
RuntimeException re =
new RuntimeException("Cannot use the baseUrl context parameter", e);
servletContext.log(re.getMessage(), re.getCause());
throw re;
}
}
servletContext.setAttribute(FEED_URL_FACTORY_ATTRIBUTE, urlFactory);
String key = servletContext.getInitParameter(RecipeUtil.DEVELOPER_KEY_PARAMETER);
GoogleBaseService service = new GoogleBaseService(applicationName, key);
mostUsedValues = new MostUsedValues(service,
urlFactory,
RecipeUtil.RECIPE_ITEMTYPE_QUERY);
initMostUsedValues(mostUsedValues, servletContext);
RecipeUtil.setMostUsedValues(servletContext, mostUsedValues);
}
public void contextDestroyed(ServletContextEvent event) {
mostUsedValues.clear();
}
/**
* Initializes a MostUsedValues object to cache the most used values
* of some attributes, suitable to be used in the web pages.
*
* @param mostUsedValues object to initialize
* @param servletContext the servlet context used by mostUsedValues
* to log exceptions
*/
public static void initMostUsedValues(MostUsedValues mostUsedValues,
ServletContext servletContext) {
long interval = 1000L * 60L * 60L; // 1 hour
mostUsedValues.cache(interval, 14, servletContext,
Recipe.CUISINE_ATTRIBUTE);
mostUsedValues.cache(interval, 16, servletContext,
Recipe.MAIN_INGREDIENT_ATTRIBUTE);
}
}
@@ -0,0 +1,395 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import com.google.api.gbase.client.FeedURLFactory;
import com.google.api.gbase.client.GoogleBaseEntry;
import com.google.api.gbase.client.GoogleBaseFeed;
import com.google.api.gbase.client.GoogleBaseQuery;
import com.google.api.gbase.client.GoogleBaseService;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* A recipe search.
*
* There will be such an object in all cases, even
* if no query has been run. This object is created
* by RecipeSearchServlet and displayed by the JSP.
*/
public class RecipeSearch {
private final GoogleBaseService service;
private Set<String> mainIngredient;
private Set<String> cuisine;
private Integer cookingTime;
private String query;
/** The query string, with the unsupported characters replaced with spaces.*/
private String queryClean;
/** The index of the first retrieved recipe. */
private int startIndex = 0;
/** Set to true to perform the search on the user's items. */
private boolean ownItems;
/** Total number of results, -1 means the query hasn't been run yet. */
protected int total = -1;
protected List<Recipe> recipes;
private static final int DEFAULT_MAX_RESULTS = 10;
private int maxResults = DEFAULT_MAX_RESULTS;
private final FeedURLFactory urlFactory;
/**
* Create a new search.
*
* @param service Google data API service
* @param urlFactory feed URL factory to be used when creating a Query
* @param ownItems true to show only the items of the authenticated user
*/
public RecipeSearch(GoogleBaseService service,
FeedURLFactory urlFactory,
boolean ownItems) {
this.service = service;
this.urlFactory = urlFactory;
this.ownItems = ownItems;
mainIngredient = null;
cuisine = null;
cookingTime = null;
query = null;
queryClean = null;
}
/**
* Checks whether the search has been run and there is at least one result.
*
* @return true if the search has been run and there is at least one result
*/
public boolean hasResults() {
return recipes != null && ! recipes.isEmpty();
}
/**
* Gets the result of the search, if it has been run.
*
* @return a list of Recipe which might be empty if the
* search has been run, or null if the search has not
* been run yet
*/
public List<Recipe> getRecipes() {
return recipes;
}
/**
* Checks if the search will be done for the authenticated user's items only.
*
* @return true if the search returns only the items that belong to the
* authenticated user
*/
public boolean isOwnItems() {
return ownItems;
}
/**
* Specifies if we are searching the authenticated user's items.
*
* @param ownItems true to search only the authenticated user's items
*/
public void setOwnItems(boolean ownItems) {
this.ownItems = ownItems;
}
/** Gets the current main ingredients, or null. */
public Set<String> getMainIngredientValues() {
return mainIngredient;
}
/** Sets the main ingredient. */
public void setMainIngredientValues(String[] mainIngredient) {
this.mainIngredient = RecipeUtil.validateValues(mainIngredient);
}
/** Gets the current cuisines, or null. */
public Set<String> getCuisineValues() {
return cuisine;
}
/** Sets the cuisine. */
public void setCuisineValues(String[] cuisine) {
this.cuisine = RecipeUtil.validateValues(cuisine);
}
/** Gets the current (maximum) cooking time, or null. */
public Integer getCookingTime() {
return cookingTime;
}
/** Sets the current maximum cooking time. */
public void setCookingTime(Integer cookingTime) {
this.cookingTime = cookingTime;
}
/** Gets the current page length. */
public int getMaxResults() {
return maxResults;
}
/** Sets the page length. */
public void setMaxResults(int maxResults) {
this.maxResults = maxResults;
}
/**
* Gets the total number of recipes that matched the query, which
* might be larger than the page length.
*
* @return the total, or -1 if the total is unknown, either because
* the query has not been run or because the total was not in
* the result
*/
public int getTotal() {
return total;
}
/**
* Gets the index of the first result to return.
*
* @return a positive value
*/
public int getStartIndex() {
return startIndex;
}
/**
* Sets the index of the first result to return.
* @param startIndex a positive value
*/
public void setStartIndex(int startIndex) {
this.startIndex = startIndex;
}
/** Gets the current page. */
public int getCurrentPage() {
return startIndex / maxResults;
}
/**
* Gets a description of the retrieved (current) interval, showing
* the index of the first item and the index of the last item.
*
* @return short description of the current page interval
*/
public String getCurrentPageInterval() {
return "" + (startIndex + 1) + " - " +
Math.min(startIndex + maxResults, total);
}
/** Gets the number of pages needed to contain all the results. */
public int getTotalPages() {
if (total == 0) {
return 0;
}
/* Using total-1, because if we have 10 results and 10 items per page,
* we still want one page.
*/
return (total - 1) / maxResults;
}
/** Runs the query and fills the result. */
public void runQuery() throws IOException, ServiceException {
GoogleBaseQuery query = createQuery();
System.out.println("Searching: " + query.getUrl());
GoogleBaseFeed feed = service.query(query);
List<Recipe> result = new ArrayList<Recipe>(maxResults);
for (GoogleBaseEntry entry : feed.getEntries()) {
result.add(new Recipe(entry));
}
this.recipes = result;
total = feed.getTotalResults();
}
/**
* Creates a GoogleBaseQuery that searches for recipes, according to
* the various properties of the RecipeSearch.
*
* @return a query to be used for querying with a
* {@link com.google.api.gbase.client.GoogleBaseService
* GoogleBaseService}
* @see com.google.api.gbase.client.GoogleBaseService#query(com.google.gdata.client.Query)
*/
private GoogleBaseQuery createQuery() {
URL queryUrl;
if (ownItems) {
queryUrl = urlFactory.getItemsFeedURL();
} else {
queryUrl = urlFactory.getSnippetsFeedURL();
}
GoogleBaseQuery query = new GoogleBaseQuery(queryUrl);
query.setMaxResults(maxResults);
if (startIndex > 0) {
// the first index is 1
query.setStartIndex(startIndex + 1);
}
query.setGoogleBaseQuery(createQueryString());
return query;
}
/**
* Creates a full text query out of the values of the query, mainIngredient,
* cuisine and cookingTime.
*
* @return a query to be used for setting the full text query of a
* {@link com.google.api.gbase.client.GoogleBaseQuery}
* @see com.google.api.gbase.client.GoogleBaseQuery#setFullTextQuery(String)
*/
private String createQueryString() {
StringBuffer retval = new StringBuffer(RecipeUtil.RECIPE_ITEMTYPE_QUERY);
if (queryClean != null) {
retval.append(queryClean);
}
appendAttributeCondition(retval, "main ingredient", mainIngredient, true);
appendAttributeCondition(retval, "cuisine", cuisine, false);
if (cookingTime != null) {
Collection<String> cookingTimes = new ArrayList<String>();
cookingTimes.add("0.." + cookingTime + " min");
cookingTimes.add("0.." + cookingTime + " minutes");
appendAttributeCondition(retval, "cooking time", cookingTimes, false);
}
return retval.toString();
}
/**
* Appends a filtering condition to a full text query.
* It is composed by simple [name: value] conditions
* joined by and AND or an OR operation.
*
* @param sb a StringBuffer for creating a full text query
* @param name name of the attributes
* @param values values the attributes have to match
* @param isAnd true if the attributes have to match all the values,
* false if the attributes have to match at least one value
*/
private static void appendAttributeCondition(StringBuffer sb,
String name,
Collection<String> values,
boolean isAnd) {
if (values != null && !values.isEmpty()) {
sb.append(" (");
Iterator iter = values.iterator();
while (iter.hasNext()) {
sb.append("[").append(name).append(": ").append(iter.next()).append("]");
if (iter.hasNext()) {
sb.append(isAnd ? " " : "|");
}
}
sb.append(")");
}
}
/** Returns true when the current page is not the first page. */
public boolean hasPreviousPage() {
return getCurrentPage() > 0;
}
/** Returns true when the current page is not the last one. */
public boolean hasNextPage() {
return getTotalPages() > getCurrentPage();
}
/** Gets the expected number of recipes for the next page. */
public int getNextPageSize() {
return Math.min(maxResults, total - (getCurrentPage() + 1) * maxResults);
}
/** Gets a description of the query used in the search. */
public StringBuffer getFilterDescription() {
StringBuffer retval = new StringBuffer();
if (queryClean != null && ! "".equals(queryClean)) {
retval.append("<b>keywords</b> are <b>").
append(queryClean).
append("</b> ");
}
addCollectionDescription(retval, cuisine, "cuisine", false);
addCollectionDescription(retval, mainIngredient, "main ingredient", true);
if (cookingTime != null) {
if (retval.length() > 0) {
retval.append("and ");
}
retval.append("<b>cooking time</b> is under <b>").
append(cookingTime).
append(" ").
append(RecipeUtil.COOKING_TIME_UNIT).
append("</b> ");
}
if (retval.length() > 0) {
retval.insert(0, "where ");
}
return retval;
}
private static void addCollectionDescription(StringBuffer buffer,
Collection<String> collection,
String name,
boolean isAnd) {
if (collection != null && ! collection.isEmpty()) {
if (buffer.length() > 0) {
buffer.append("and ");
}
buffer.append("<b>").append(name).append("</b> is");
Iterator<String> iter = collection.iterator();
while (iter.hasNext()) {
buffer.append(" <b>").append(iter.next()).append("</b> ");
if (iter.hasNext()) {
buffer.append(isAnd ? "and " : "or ");
}
}
}
}
/**
* Sets the query string.
*
* @param query the query string, as provided by user
* @throws NullPointerException if the {@code query} is null.
*/
public void setQuery(String query) {
if (query != null) {
this.query = query;
this.queryClean = RecipeUtil.cleanQueryString(query);
} else {
throw new NullPointerException("Query must not be null.");
}
}
/**
* Returns the original query string, as specified in the
* {@link #setQuery(String)} method.
*/
public String getQuery() {
return query;
}
}
@@ -0,0 +1,179 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import com.google.api.gbase.client.FeedURLFactory;
import com.google.api.gbase.client.GoogleBaseService;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Setup a {@link RecipeSearch} object and optionally fill it.
*/
@SuppressWarnings("serial")
public class RecipeSearchServlet extends HttpServlet {
private static final String START_INDEX_PARAMETER = "startIndex";
private static final String MAX_RESULTS_PARAMETER = "maxResults";
private static final String QUERY_PARAMETER = "query";
public static final String DISPLAY_JSP = "/WEB-INF/recipeSearch.jsp";
protected boolean ownItems;
protected FeedURLFactory urlFactory;
@Override
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
ServletContext context = servletConfig.getServletContext();
urlFactory = (FeedURLFactory)
context.getAttribute(RecipeListener.FEED_URL_FACTORY_ATTRIBUTE);
String scope = servletConfig.getInitParameter("scope");
ownItems = "own".equals(scope);
}
@Override
public void destroy() {
super.destroy();
}
/**
* Runs a recipe search.
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
GoogleBaseService service = RecipeUtil.getGoogleBaseService(request,
this.getServletContext());
RecipeSearch recipeSearch;
try {
if (request.getParameter("query") == null) {
recipeSearch = new RecipeSearch(service, urlFactory, ownItems);
} else {
recipeSearch = createRecipeSearch(service, request);
}
recipeSearch.runQuery();
} catch (RecipeValidationException rve) {
// internal exception in the argument handling (possibly incorrect args)
RecipeUtil.forwardToErrorPage(request, response, rve.getMessage());
return;
} catch (ServiceException se) {
// exception comming from Google Base
RecipeUtil.logServiceException(this, se);
RecipeUtil.forwardToErrorPage(request, response, se);
return;
}
RecipeUtil.setRecipeSearch(request, recipeSearch);
// Forward to the JSP
request.getRequestDispatcher(DISPLAY_JSP).forward(request, response);
}
/**
* Creates and fills in a {@link RecipeSearch} object based on the content
* of the {@code request}.
*
* @param service the object used to connect to the Google Base service
* @param request the representation of the Http request
* @throws RecipeValidationException if a parameter from the request has an
* invalid value (cooking time, start index, max results are not valid
* numbers).
*/
private RecipeSearch createRecipeSearch(GoogleBaseService service,
HttpServletRequest request)
throws RecipeValidationException {
RecipeSearch search = new RecipeSearch(service, urlFactory, ownItems);
String query = request.getParameter(QUERY_PARAMETER);
if (isSet(query)) {
search.setQuery(query);
}
String[] mainIngredient = request.getParameterValues(
RecipeUtil.MAIN_INGREDIENT_PARAMETER);
if (isSet(mainIngredient)) {
search.setMainIngredientValues(mainIngredient);
}
String[] cuisine = request.getParameterValues(
RecipeUtil.CUISINE_PARAMETER);
if (isSet(cuisine)) {
search.setCuisineValues(cuisine);
}
String cookingTime = request.getParameter(
RecipeUtil.COOKING_TIME_PARAMETER);
if (isSet(cookingTime)) {
try {
search.setCookingTime(new Integer(cookingTime));
} catch (NumberFormatException e) {
throw new RecipeValidationException(String.format(
"Cooking time is not a number (%s).", cookingTime));
}
}
String startIndex = request.getParameter(START_INDEX_PARAMETER);
if (isSet(startIndex)) {
try {
search.setStartIndex(Integer.parseInt(startIndex));
} catch (NumberFormatException e) {
throw new RecipeValidationException(String.format(
"Start index is not a number (%s).", startIndex));
}
}
String maxResults = request.getParameter(MAX_RESULTS_PARAMETER);
if (isSet(maxResults)) {
try {
search.setMaxResults(Integer.parseInt(maxResults));
} catch (NumberFormatException e) {
throw new RecipeValidationException(String.format(
"Max results is not a number (%s).", maxResults));
}
}
search.setOwnItems(ownItems);
return search;
}
private boolean isSet(String parameter) {
return parameter != null && !"".equals(parameter);
}
private boolean isSet(String[] parameter) {
return parameter != null && parameter.length > 0;
}
}
@@ -0,0 +1,269 @@
/* Copyright (c) 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
import com.google.api.gbase.client.GoogleBaseService;
import com.google.api.gbase.client.ServiceErrors;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Contains the names of the HTML input fields used to edit a recipe.
* Has methods that are generally useful, for example for logging
* or validating values.
* Has methods to extract values from servlet context init parameters.
* Has methods used for passing objects from a servlet to a JSP.
*/
public class RecipeUtil {
public static final String ID_PARAMETER = "oid";
public static final String APPLICATION_NAME_PARAMETER = "applicationName";
public static final String TITLE_PARAMETER = "title";
public static final String URL_PARAMETER = "url";
public static final String DESCRIPTION_PARAMETER = "description";
public static final String MAIN_INGREDIENT_PARAMETER = "mainIngredient";
public static final String CUISINE_PARAMETER = "cuisine";
public static final String COOKING_TIME_PARAMETER = "cookingTime";
public static final String DEVELOPER_KEY_PARAMETER = "key";
public static final String COOKING_TIME_UNIT = "minutes";
public static final String RECIPE_ATTRIBUTE = "recipe";
public static final String RECIPESEARCH_ATTRIBUTE = "recipeSearch";
public static final String RECIPESEARCH_ERROR = "recipeSearchError";
public static final String RECIPESEARCH_ERROR_DESCRIPTION =
"recipeSearchErrorDescription";
public static final String RECIPESEARCH_ERROR_OBJECT =
"recipeSearchErrorService";
public static final String MESSAGE_ATTRIBUTE = "message";
public static final String RECIPE_ITEMTYPE_QUERY =
"[item type : recipe | recipes]";
/** Pattern used for finding the unsupported characters in the query string.*/
private static final Pattern QUERY_REPLACE_PATTERN =
Pattern.compile("\\p{Punct}");
public static final String ERROR_JSP = "/WEB-INF/recipeError.jsp";
/**
* Builds a HashSet containing the specified values,
* filtering the null and empty ones.
*
* @param values usually an array returned by request.getParameterValues()
* @return a HashSet containing the nonempty values
*/
public static Set<String> validateValues(String[] values) {
Set<String> valuesList = new HashSet<String>();
if (values != null) {
for (String value : values) {
value = cleanQueryString(value);
if (value != null && ! "".equals(value)) {
valuesList.add(value);
}
}
}
return valuesList;
}
/**
* Cleans the {@code searchString} set by the user, by removing the special
* punctuation characters not allowed directly in a query.
*
* @param searchString the string set by the user
* @return the String to be used for executing the query to Google Base.
*/
public static String cleanQueryString(String searchString) {
Matcher matcher = QUERY_REPLACE_PATTERN.matcher(searchString);
return matcher.replaceAll(" ").trim();
}
/**
* Logs an exception in a convenient format,
* using the log method of a servlet context.
*
* @param servlet the servlet used to log the exception
* @param e exception to be logged
*/
public static void logServiceException(HttpServlet servlet,
ServiceException e) {
if (e.getResponseBody() != null) {
// Log the full error message and response code.
servlet.log(e.getMessage() + " " +
e.getHttpErrorCodeOverride() + " " +
e.getResponseContentType() + ": " +
e.getResponseBody(), e);
}
}
/**
* Sets a RecipeSearch as an attribute of a HttpServletRequest.
*
* @param request a request that will be passed to a JSP
* @param results the RecipeSearch, executed or not
*/
public static void setRecipeSearch(HttpServletRequest request,
RecipeSearch results) {
request.setAttribute(RECIPESEARCH_ATTRIBUTE, results);
}
/**
* Gets from a HttpServletRequest a RecipeSearch that was previously set
* using {@link #setRecipeSearch}.
* If it is missing, a NullPointerException is thrown.
*
* @param request a request passed from a Servlet
* @return a non-null RecipeSearch
*/
public static RecipeSearch getRecipeSearch(HttpServletRequest request) {
RecipeSearch results = (RecipeSearch)request.getAttribute(
RECIPESEARCH_ATTRIBUTE);
if (null == results) {
throw new NullPointerException("recipe search results are missing");
}
return results;
}
/**
* Gets the error message, or {@code null} if no error message was set in the
* request.
*
* @param request the request in which the method will locate the error
* @return the error message, or {@code null} if no message was set
*/
public static String getRecipeError(HttpServletRequest request) {
return (String)request.getAttribute(RECIPESEARCH_ERROR);
}
/**
* Gets the error message, or {@code null} if no error message was set in the
* request.
*
* @param request the request in which the method will locate the error
* @return the error message, or {@code null} if no message was set
*/
public static String getRecipeErrorDescription(HttpServletRequest request) {
return (String)request.getAttribute(RECIPESEARCH_ERROR_DESCRIPTION);
}
/**
* Gets the errors obtained from Google Base, or {@code null} if no service
* error was set in the request.
*
* @param request the request in which the method will locate the error
* @return the service errors, or {@code null} if no service errors were set
*/
public static ServiceErrors getRecipeErrorObject(HttpServletRequest request) {
return (ServiceErrors)request.getAttribute(RECIPESEARCH_ERROR_OBJECT);
}
/**
* Forwards the request to the error page, for displaying the specified
* {@code errorMessage} and the {@code description}. No service errors will
* be displayed.
*
* @param request the request object
* @param response the response object
* @param errorMessage the error message to be displayed
* @param description the description of the error message, {@code null} if
* no description should be displayed.
* @throws ServletException
* @throws IOException
*/
public static void forwardToErrorPage(HttpServletRequest request,
HttpServletResponse response, String errorMessage)
throws ServletException, IOException {
request.setAttribute(RECIPESEARCH_ERROR, errorMessage);
request.getRequestDispatcher(ERROR_JSP).forward(request, response);
}
/**
* Forwards the request to the error page, for displaying the information
* contained by the {@code se} parameter. This method registers the service
* errors too, using a {@link ServiceErrors} object.
*
* @param request the request object
* @param response the response object
* @param se the service error containing the information for the error page
* @throws ServletException
* @throws IOException
*/
public static void forwardToErrorPage(HttpServletRequest request,
HttpServletResponse response, ServiceException se)
throws ServletException, IOException {
request.setAttribute(RECIPESEARCH_ERROR, se.getMessage());
request.setAttribute(RECIPESEARCH_ERROR_DESCRIPTION, se.getResponseBody());
request.setAttribute(RECIPESEARCH_ERROR_OBJECT, new ServiceErrors(se));
request.getRequestDispatcher(ERROR_JSP).forward(request, response);
}
/**
* Gets the GoogleBaseService object created by {@link AuthenticationFilter}
* or creates a new one if <code>AuthenticationFilter</code> has not been
* applied yet.
*
* @param req
* @param servletContext
* @return a GoogleBaseService object
*/
public static GoogleBaseService getGoogleBaseService(HttpServletRequest req,
ServletContext servletContext) {
GoogleBaseService service;
service = (GoogleBaseService) req.getAttribute(
AuthenticationFilter.SERVICE_ATTRIBUTE);
if (service == null) {
service = new GoogleBaseService(
servletContext.getInitParameter(APPLICATION_NAME_PARAMETER),
servletContext.getInitParameter(DEVELOPER_KEY_PARAMETER));
req.setAttribute(AuthenticationFilter.SERVICE_ATTRIBUTE, service);
}
return service;
}
/**
* Gets a MostUsedValues that was previously set using
* {@link #setMostUsedValues}.
*
* @param servletContext
* @return a non-null initialized MostUsedValues
*/
public static MostUsedValues getMostUsedValues(ServletContext servletContext)
throws ServletException {
MostUsedValues mostUsedValues = (MostUsedValues)
servletContext.getAttribute(RecipeListener.MOST_USED_VALUES_ATTRIBUTE);
if (null == mostUsedValues) {
throw new ServletException("Most used values cache is missing");
}
return mostUsedValues;
}
public static void setMostUsedValues(ServletContext servletContext,
MostUsedValues mostUsedValues) {
servletContext.setAttribute(RecipeListener.MOST_USED_VALUES_ATTRIBUTE,
mostUsedValues);
}
}
@@ -0,0 +1,27 @@
/* Copyright (c) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.gbase.recipe;
/**
* Exception thrown when an invalid parameter is received by the Recipe Demo
* Application.
*/
public class RecipeValidationException extends Exception {
public RecipeValidationException(String message) {
super(message);
}
}
@@ -0,0 +1,3 @@
<div class="demolabel">
This recipe book is a demo of the Google Base Data API. <br/><a href="http://code.google.com/apis/base">Learn more and download API tools</a>.
</div>
@@ -0,0 +1,7 @@
<div id="footer">
<a href="http://code.google.com/apis/base">Google Base Data API</a> -
<a href="http://code.google.com/apis/base/terms.html">Terms and Conditions</a> -
<a href="http://base.google.com">Google Base</a> -
<a href="http://www.google.com">Google home</a>
<p id="copyright">&copy; Google 2006</p>
</div>
@@ -0,0 +1,68 @@
<%--
Copyright (c) 2006 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page import="sample.gbase.recipe.DisplayUtils"%>
<%@ page import="sample.gbase.recipe.Recipe"%>
<%@ page import="sample.gbase.recipe.RecipeSearch"%>
<%@ page import="sample.gbase.recipe.RecipeUtil"%>
<%RecipeSearch searchResults = RecipeUtil.getRecipeSearch(request);%>
<div id="leftnav">
<div id="search">
<form name="searchForm" action="recipe<%= searchResults.isOwnItems() ? "List" : "Search" %>" method="GET"><h2>Find a recipe</h2>
<table border="0" cellpadding="0" cellspacing="0" id="advancedsearch">
<tr><th colspan="2">With the word or phrase</th></tr>
<tr>
<td><input type="text" name="query" size="25" class="txt"
value='<%=DisplayUtils.escape(searchResults.getQuery()) %>'>
</td>
</tr>
<tr>
<th>Cuisine type</th>
</tr>
<tr>
<td><%DisplayUtils.printCheckboxes(out,
RecipeUtil.CUISINE_PARAMETER,
RecipeUtil.getMostUsedValues(pageContext.getServletContext()).getMostUsedValuesForAttribute(Recipe.CUISINE_ATTRIBUTE),
searchResults.getCuisineValues()); %></td>
</tr>
<tr>
<th>Main ingredient</th>
</tr>
<tr>
<td><%DisplayUtils.printCheckboxes(out,
RecipeUtil.MAIN_INGREDIENT_PARAMETER,
RecipeUtil.getMostUsedValues(pageContext.getServletContext()).getMostUsedValuesForAttribute(Recipe.MAIN_INGREDIENT_ATTRIBUTE),
searchResults.getMainIngredientValues()); %></td>
</tr>
<tr>
<th>Cooking time</th>
</tr>
<tr>
<td><input name="cookingTime" type="text" size="4"
value='<%=DisplayUtils.escape(searchResults.getCookingTime()) %>'
> <%=RecipeUtil.COOKING_TIME_UNIT %></td>
</tr>
</table>
<br/>
<button onclick="document.searchForm.startIndex.value='0'; document.searchForm.submit();"
style="font-weight:bold; -moz-border-radius:3px;border: 2px outset #bbbbbb;font-size:124%; padding:2px 1em;">Search</button>
<input type="hidden" name="startIndex" value="0"></form>
</div>
<p id="poweredby">
&nbsp;Powered by<br>
<a href="http://base.google.com/"><img src="googleBase.gif" border="0" alt="Google Base" vspace="2"></a>
</p>
</div>
@@ -0,0 +1,71 @@
<%--
Copyright (c) 2006 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page import="sample.gbase.recipe.DisplayUtils"%>
<%@ page import="sample.gbase.recipe.Recipe"%>
<%@ page import="sample.gbase.recipe.RecipeUtil"%>
<html>
<head>
<title>Google Base API Demo: Recipe Book</title>
<link rel="stylesheet" href="style.css"/>
</head>
<body>
<div id="content">
<%@ include file="demoLabel.inc" %>
<%Recipe recipe = (Recipe) request.getAttribute(RecipeUtil.RECIPE_ATTRIBUTE); %>
<h1><a href="recipeSearch" class="on">All Recipes</a> &gt; <%=DisplayUtils.escape(recipe.getTitle()) %></h1>
<%@ include file="leftNav.jsp" %>
<div id="body" class="main">
<div id="singleitem">
<h3 class="itemtitle"><%=DisplayUtils.escape(recipe.getTitle()) %></h3>
<h4 class="postdate"><%=recipe.getPostedOnAsString(true) %></h4>
<%if (recipe.hasCuisine()) { %>
<h5>Cuisine: <%=DisplayUtils.printList(recipe.getCuisine()) %></h5><br/>
<%} %>
<%if (recipe.hasCookingTime()) { %>
<h5>Cooking time: <%=recipe.getCookingTime().getValue() %> <%=DisplayUtils.escape(recipe.getCookingTime().getUnit()) %></h5>
<%} %>
<%if (recipe.hasMainIngredient()) { %>
<div class="ingredients">
<ul>
<%for (String ingredient : recipe.getMainIngredient()) { %>
<li><%=DisplayUtils.escape(ingredient) %></li>
<%} %>
</ul>
</div>
<%} %>
<br/>
<div class="preparation">
<%=DisplayUtils.escape(recipe.getDescription()) %>
</div>
</div>
</div>
<br clear="all">
<%@ include file="demoLabel.inc" %>
</div>
<%@ include file="footer.inc" %>
</body>
</html>
@@ -0,0 +1,112 @@
<%--
Copyright (c) 2006 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page import="sample.gbase.recipe.DisplayUtils"%>
<%@ page import="sample.gbase.recipe.Recipe"%>
<%@ page import="sample.gbase.recipe.RecipeUtil"%>
<html>
<head>
<title>Google Base API Demo: Recipe Book</title>
<link rel="stylesheet" href="style.css">
<style type="text/css">
.main {
margin-left: 0;
}
</style>
</head>
<body>
<div id="content">
<%@ include file="demoLabel.inc" %>
<%Recipe recipe = (Recipe) request.getAttribute(RecipeUtil.RECIPE_ATTRIBUTE); %>
<%String message = (String) request.getAttribute(RecipeUtil.MESSAGE_ATTRIBUTE); %>
<h1><a href="recipeList" class="on">My Recipes</a> &gt; <%=recipe.isNew() ? "Add" : "Update" %> a recipe</h1>
<div id="body" class="main">
<div id="newitem">
<%=message %>
<form name="newitem" method="POST" action="recipe<%=recipe.isNew() ? "Add" : "Update?" + RecipeUtil.ID_PARAMETER + "=" + DisplayUtils.escape(recipe.getId()) %>"/>
<table cellpadding="0" cellspacing="0" border="0" id="createrecipe">
<tr>
<th>Recipe title *</th>
<td><input type="text" name="<%=RecipeUtil.TITLE_PARAMETER %>"
value="<%=DisplayUtils.escape(recipe.getTitle()) %>"
class="txt" size="35"></td>
</tr>
<tr>
<th>Cuisine type *</th>
<td>
<%DisplayUtils.printCheckboxes(out,
RecipeUtil.CUISINE_PARAMETER,
RecipeUtil.getMostUsedValues(pageContext.getServletContext()).getMostUsedValuesForAttribute(Recipe.CUISINE_ATTRIBUTE),
recipe.getCuisine()); %>
</td>
</tr>
<tr>
<th>Instructions *</th>
<td><textarea name="<%=RecipeUtil.DESCRIPTION_PARAMETER %>"
cols="40" rows="8"
><%=DisplayUtils.escape(recipe.getDescription()) %></textarea>
</td>
</tr>
<tr>
<th>Main ingredients *</th>
<td>
<%DisplayUtils.printCheckboxes(out,
RecipeUtil.MAIN_INGREDIENT_PARAMETER,
RecipeUtil.getMostUsedValues(pageContext.getServletContext()).getMostUsedValuesForAttribute(Recipe.MAIN_INGREDIENT_ATTRIBUTE),
recipe.getMainIngredient()); %>
</td>
</tr>
<tr>
<th>URL</th>
<td><input type="text" name="<%=RecipeUtil.URL_PARAMETER %>"
value="<%=DisplayUtils.escape(recipe.getUrl()) %>"
class="txt" size="40"></td>
</tr>
<tr>
<th>Cooking time</th>
<td><input type="text" name="<%=RecipeUtil.COOKING_TIME_PARAMETER %>"
value="<%=recipe.hasCookingTime() ? recipe.getCookingTime().getValue() : "" %>"
class="txt" size="4"> <%=RecipeUtil.COOKING_TIME_UNIT %></td>
</tr>
</table>
<p>
This recipe will be publicly viewable on the internet once you click "Publish". <br> <br>
<input type="submit" value="Publish this recipe" style="font-weight:bold;"> &nbsp;
<button onclick="history.go(-1); return false;">Cancel</button>
</p>
<p>* Mandatory attributes</p>
</form>
</div>
</div>
<br clear="all">
<%@ include file="demoLabel.inc" %>
</div>
<%@ include file="footer.inc" %>
</body>
</html>
@@ -0,0 +1,78 @@
<%--
Copyright (c) 2007 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page import="com.google.api.gbase.client.ServiceErrors"%>
<%@ page import="sample.gbase.recipe.RecipeUtil"%>
<%@page import="sample.gbase.recipe.DisplayUtils"%>
<%@page import="com.google.api.gbase.client.ServiceError"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Google Base API demo: Recipe Book</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="content">
<%@ include file="demoLabel.inc" %>
<%
String error = RecipeUtil.getRecipeError(request);
String description = RecipeUtil.getRecipeErrorDescription(request);
ServiceErrors serviceErrors = RecipeUtil.getRecipeErrorObject(request);
%>
<div style="height:70%; padding-top: 20px;">
An error has occured. Hit the browser's Back button for continuing to use
the Recipe Demo Application. <br/>
<%if (error != null) { %>
<div class="errordiv">
<span class="errormessage"><%= DisplayUtils.escape(error) %></span>
<br/>
</div>
<%} // has error %>
<%if (description != null) { %>
<div class="errordiv">
Detailed information about the error: <br/>
<pre><%= DisplayUtils.escape(description) %></pre>
</div>
<%} // has description %>
<%if (serviceErrors != null) { %>
<div class="errordiv">
Detailed information about the Google Base service error: <br/>
<ul>
<%for (ServiceError serviceError : serviceErrors.getAllErrors()) { %>
<li> <%= DisplayUtils.escape(serviceError.getType()) %> : <%= DisplayUtils.escape(serviceError.getReason()) %>
<%} // for each service error %>
</ul>
<%if (serviceErrors.getAllErrors().size() == 0) { %>
<i>No service errors could be parsed</i>
<%} // if no service errors registered %>
</div>
<%} // has errors object %>
</div>
<br clear="all">
<%@ include file="demoLabel.inc" %>
</div>
<%@ include file="footer.inc" %>
</body>
</html>
@@ -0,0 +1,138 @@
<%--
Copyright (c) 2006 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page import="sample.gbase.recipe.DisplayUtils"%>
<%@ page import="sample.gbase.recipe.Recipe"%>
<%@ page import="sample.gbase.recipe.RecipeSearch"%>
<%@ page import="sample.gbase.recipe.RecipeUtil"%>
<%@ page import="java.util.Iterator"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Google Base API demo: Recipe Book</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="content">
<%@ include file="demoLabel.inc" %>
<%RecipeSearch recipeSearch = RecipeUtil.getRecipeSearch(request); %>
<%if (recipeSearch.isOwnItems()) { %>
<a href="recipeSearch" class="toplink">All recipes</a>
<%} else { %>
<a href="recipeList" class="toplink">My recipes</a>
<%} %>
<%if (recipeSearch.isOwnItems()) { %>
<a href="recipeAdd" class="toplink">Add a recipe</a>
<%} %>
<h1>
<%if (recipeSearch.isOwnItems()) { %>
<a href="recipeList" class="on">My Recipes</a>
<%} else { %>
<a href="recipeSearch" class="on">All Recipes</a>
<%} %>
&gt; Search
<%if (recipeSearch.getTotal() > 0) { %>
results
<%} %>
</h1>
<%@ include file="leftNav.jsp" %>
<div id="body" class="main">
<div id="searchresults">
<script language="JavaScript">
function gotoPage(index) {
document.searchForm.reset();
document.searchForm.startIndex.value =
index*<%=recipeSearch.getMaxResults()%>;
document.searchForm.submit();
}
function narrowSearchByIngredient(ingredient) {
document.searchForm.reset();
document.searchForm.othermainIngredient.value = ingredient;
document.searchForm.submit();
}
function confirmDelete(oid) {
if (confirm("Do you really want to delete this recipe?")) {
window.location = "recipeDelete?oid=" + oid;
}
}
</script>
<%if (recipeSearch.getTotal() >= 0) {%>
<%if (recipeSearch.getTotal() == 0) {%>
<h4>Your search for recipes
<%=recipeSearch.getFilterDescription() %>
did not match any recipes.</h4>
<%} %>
<%if (recipeSearch.hasResults()) {%>
<h4><%=recipeSearch.getTotal()%> recipes
<%=recipeSearch.getFilterDescription() %>
- showing <%=recipeSearch.getCurrentPageInterval()%></h4>
<%for (Iterator iter = recipeSearch.getRecipes().iterator(); iter.hasNext(); ) {%>
<%Recipe recipe = (Recipe)iter.next();%>
<p>
<a href="recipeDisplay?oid=<%=recipe.getId()%>" class="m"><%=DisplayUtils.escape(recipe.getTitle()).toUpperCase() %></a>
Main ingredient:
<%for (Iterator<String> ingredientIter = recipe.getMainIngredient().iterator(); ingredientIter.hasNext(); ) { %>
<%String ingredient = ingredientIter.next(); %>
<a href="javascript:narrowSearchByIngredient('<%=ingredient%>')" class="r"><%=ingredient%></a><%=ingredientIter.hasNext() ? ", " : "" %>
<%} %>
<br>
posted on <%=recipe.getPostedOnAsString(false)%>
by <%=DisplayUtils.escape(recipe.getPostedBy())%><br>
<%if (recipeSearch.isOwnItems()) { %>
<a href="recipeUpdate?oid=<%=recipe.getId() %>">Update</a> |
<a href="javascript:confirmDelete('<%=recipe.getId() %>')">Delete</a>
<%} %>
</p>
<%} // for recipe in recipeSearch.getRecipes()%>
<%} // if recipeSearch.hasResults%>
<%if (recipeSearch.getTotal() > recipeSearch.getMaxResults()) { //results don't fit on one page?%>
<p class="pagination">
Results <%=recipeSearch.getCurrentPageInterval() %> of <%=recipeSearch.getTotal() %><br/>
<%if (recipeSearch.hasPreviousPage()) {%>
<a href="javascript:gotoPage(<%=recipeSearch.getCurrentPage() - 1 %>)"
style="margin-right:1em;"
class="next">&laquo; Previous <%=recipeSearch.getMaxResults() %></a>
<%} %>
<%if (recipeSearch.hasNextPage()) {%>
<a href="javascript:gotoPage(<%=recipeSearch.getCurrentPage() + 1 %>)"
class="next">Next <%=recipeSearch.getNextPageSize() %> &raquo;</a>
<%} %>
</p>
<%} // pages? %>
<%} //total >= 0 %>
</div>
</div>
<br clear="all">
<%@ include file="demoLabel.inc" %>
</div>
<%@ include file="footer.inc" %>
</body>
</html>
@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Google Base Recipe Search</display-name>
<description>Search Recipes on Google Base</description>
<context-param>
<param-name>baseUrl</param-name>
<param-value>http://www.google.com/base/</param-value>
<description>URL of the google base server to connect to.</description>
</context-param>
<context-param>
<param-name>applicationName</param-name>
<param-value>Google-RecipeDemo-1.0</param-value>
<description>Application name used when authenticating.</description>
</context-param>
<context-param>
<param-name>key</param-name>
<param-value></param-value>
<description>Developer key used to authenticate against the Google Base data API servers.</description>
</context-param>
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>sample.gbase.recipe.AuthenticationFilter</filter-class>
<init-param>
<param-name>authsubProtocol</param-name>
<param-value>https</param-value>
<description>Protocol to be used when connecting to AuthSub.</description>
</init-param>
<init-param>
<param-name>authsubHostname</param-name>
<param-value>www.google.com</param-value>
<description>Hostname of the authentication server.</description>
</init-param>
</filter>
<listener>
<listener-class>sample.gbase.recipe.RecipeListener</listener-class>
</listener>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<servlet-name>OwnRecipeSearchServlet</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<servlet-name>RecipeAddServlet</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<servlet-name>RecipeUpdateServlet</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<servlet-name>RecipeDeleteServlet</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>AllRecipeSearchServlet</servlet-name>
<servlet-class>sample.gbase.recipe.RecipeSearchServlet</servlet-class>
<init-param>
<param-name>scope</param-name>
<param-value>all</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>OwnRecipeSearchServlet</servlet-name>
<servlet-class>sample.gbase.recipe.RecipeSearchServlet</servlet-class>
<init-param>
<param-name>scope</param-name>
<param-value>own</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>RecipeAddServlet</servlet-name>
<servlet-class>sample.gbase.recipe.RecipeActionServlet</servlet-class>
<init-param>
<param-name>action</param-name>
<param-value>add</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>RecipeUpdateServlet</servlet-name>
<servlet-class>sample.gbase.recipe.RecipeActionServlet</servlet-class>
<init-param>
<param-name>action</param-name>
<param-value>update</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>RecipeDeleteServlet</servlet-name>
<servlet-class>sample.gbase.recipe.RecipeActionServlet</servlet-class>
<init-param>
<param-name>action</param-name>
<param-value>delete</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>RecipeDisplayServlet</servlet-name>
<servlet-class>sample.gbase.recipe.RecipeDisplayServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AllRecipeSearchServlet</servlet-name>
<url-pattern>/recipeSearch</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>OwnRecipeSearchServlet</servlet-name>
<url-pattern>/recipeList</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RecipeAddServlet</servlet-name>
<url-pattern>/recipeAdd</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RecipeUpdateServlet</servlet-name>
<url-pattern>/recipeUpdate</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RecipeDeleteServlet</servlet-name>
<url-pattern>/recipeDelete</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RecipeDisplayServlet</servlet-name>
<url-pattern>/recipeDisplay</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

+1
View File
@@ -0,0 +1 @@
<%response.sendRedirect(request.getContextPath() + "/recipeSearch");%>
+89
View File
@@ -0,0 +1,89 @@
body {
font-family: geneva,tahoma,arial,sans-serif; font-size: 83%;
background: #FFFFCC;
text-align:center;
vertical-align:top;
}
td {
font-size: 78%;
vertical-align:top;
}
th {
font-size: 78%;
text-align: left;
vertical-align:top;
}
a:link { color: #0000CC; }
a:visited { color:#0000CC; }
a.on:visited { color: #0000CC; }
.toplink:visited { color: #0000CC; }
.demolabel a:link { color: #0000E0!important; font-weight:bold; }
a.m { display:block; }
a.r { color: #6666cc; text-decoration:none;}
#search th { padding: 1em 0 2px 0; }
#newitem th { padding: .8ex 1em 2px 0px; }
#newitem td { padding: 2px 1em 2px 0px; }
.demolabel { background: #ffffff; font-size: 124%; text-align:center; padding: .5em; -moz-border-radius:4px; border: 1px solid #EEEE66;}
input.txt, textarea { border: 1px inset #999999; -moz-border-radius: 3px; padding:2px;}
textarea { font-family: geneva, tahoma, arial, sans-serif; font-size: 105%; }
label { cursor: pointer; }
ul.inputlist {
padding-left:0;
list-style-type:none;
}
.inputlist li { padding-right: 1em; }
#content { text-align:left; margin: 0 15% 0 15%;}
#leftnav {
width: 30%;
float:left;
}
#search {
border:1px solid #EEEE66;
-moz-border-radius:12px;
background: #FFFFe0;
padding:.5em;
}
#rpp { float:right; }
.next { font-weight: bold; font-size: 124%; }
.main {
margin-left: 33%;
border: 1px solid #EEEE66;
-moz-border-radius:8px;
background: #FFFFe0;
padding: 1em;
}
#footer { margin-top: 2em; }
#footer p { margin:0; }
h2 { margin:0; }
h3 { margin:0; }
h4 { margin:0; font-size: 100%; font-weight: normal;}
h5 { margin:0; font-weight: normal; display:inline; font-size: 100%;}
.toplink {
float:right;
font-weight:bold;
margin-left:1.6em;
padding-top:1.6em;
font-size: 124%;
}
#poweredby {
font-size: 82%;
margin-left:3%;
}
#poweredby img { height: 27px; width: 75px; }
.instructions { font-size: 78%; color: #000000;}
.instructions i { font-style: normal; color:#008000; }
.errormessage { font-weight: bold; color: #CC1100; margin-bottom: 0.5em; }
.errordiv { padding-top: 35px; }