Google Code предлагается на следующих языках: English – Español – 日本語 – 한국어 – Português – Pусский – 中文(简体) – 中文(繁體)
Хранение данных в масштабируемом веб-приложении может быть непростой задачей. В определенный момент времени пользователь может взаимодействовать с любым из десятков веб-серверов, а его следующий запрос может отправиться уже не на тот веб-сервер, который обрабатывал предыдущий. Все веб-серверы должны взаимодействовать с данными, которые также распределены по десяткам компьютеров, возможно расположенным в разных точках планеты.
Благодаря Google App Engine беспокоиться об этом не надо. Инфраструктура App Engine занимается распределением, копированием и регулированием нагрузки данных с помощью простого API, а вы получаете мощный механизм поиска и транзакции.
Хранилище данных App Engine – это одна из нескольких служб, предоставляемых App Engine с помощью двух API: стандартного API и API низкого уровня. С помощью стандартных API можно легко переносить приложение в другие среды размещения и другие технологии баз данных, если нужно. Стандартные API "разъединяют" приложение и службы App Engine. Службы App Engine также предоставляют API низкого уровня, непосредственно предоставляющие возможности службы. API низкого уровня можно использовать для реализации новых адаптерных интерфейсов или просто использовать API прямо в приложении.
App Engine поддерживает два стандарта API для хранилища данных: Объекты данных Java (JDO) и API постоянства Java (JPA). Эти интерфейсы предоставляются платформой DataNucleus Access Platform, реализацией стандартов постоянства Java с открытым исходным кодом с адаптером для хранилища данных App Engine.
Для гостевой книги мы используем интерфейс JDO для получения и отправки сообщений пользователей.
DataNucleus Access Platform нужен файл конфигурации, который предписывает ему использовать хранилище данных App Engine в качестве сервера для реализации JDO. В окончательном WAR тот файл называется jdoconfig.xml и находится в каталоге war/WEB-INF/classes/META-INF/.
При использовании Eclipse этот файл уже создан в виде src/META-INF/jdoconfig.xml. Этот файл автоматически копируется в war/WEB-INF/classes/META-INF/ при сборке проекта.
Если вы не используете Eclipse, можно прямо создать каталог war/WEB-INF/classes/META-INF/ или создать его с помощью процесса сборки и скопировать файл конфигурации из другого места. Сценарий сборки Ant, описанный в статье Использование Apache Ant копирует этот файл из src/META-INF/.
У файла jdoconfig.xml должно быть следующее содержание:
<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">
<persistence-manager-factory name="transactions-optional">
<property name="javax.jdo.PersistenceManagerFactoryClass"
value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
<property name="javax.jdo.option.ConnectionURL" value="appengine"/>
<property name="javax.jdo.option.NontransactionalRead" value="true"/>
<property name="javax.jdo.option.NontransactionalWrite" value="true"/>
<property name="javax.jdo.option.RetainValues" value="true"/>
<property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
</persistence-manager-factory>
</jdoconfig>
При создании классов JDO используются аннотации Java, чтобы описать хранение экземпляров в хранилище данных и их воссоздание при получении их из хранилища данных. Access Platform соединяет классы данных с реализацией с помощью этапа обработки после компиляции, который называется в DataNucleus "улучшением" классов.
При использовании Eclipse и плагина Google, последний выполняет этап улучшения классов JDO автоматически в процессе сборки.
При использовании сценария сборки Ant, описанного в статье Использование Apache Ant, сценарий сборки включает нужны этап улучшения.
Дополнительные сведения об улучшении классов JDO см. в статье Использование JDO.
JDO позволяет хранить объекты Java (иногда называемые простыми старыми объектами Java или POJO) в любом хранилище данных с совместимым с JDO адаптером, например платформе DataNucleus Access Platform. SDK App Engine включает плагин платформы Access Platform для хранилища данных App Engine. Это значит, что можно сохранять экземпляры классов, определенные в хранилище данных App Engine и извлекать их как объекты с помощью JDO API. Инструкции о том, как хранить и восстанавливать экземпляры классов, даются JDO с помощью аннотаций Java.
Создадим класс Greeting, представляющий отдельные сообщения, размещенные в гостевой книге.
Создайте класс под названием Greeting в пакете guestbook. (Для тех, кто не пользуется Eclipse: создайте файл Greeting.java в каталоге src/guestbook/.) Поместите в исходный файл следующее содержание:
package guestbook;
import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.users.User;
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Greeting {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private User author;
@Persistent
private String content;
@Persistent
private Date date;
public Greeting(User author, String content, Date date) {
this.author = author;
this.content = content;
this.date = date;
}
public Long getId() {
return id;
}
public User getAuthor() {
return author;
}
public String getContent() {
return content;
}
public Date getDate() {
return date;
}
public void setAuthor(User author) {
this.author = author;
}
public void setContent(String content) {
this.content = content;
}
public void setDate(Date date) {
this.date = date;
}
}
Этот простой класс определяет 3 свойства приветствия: author, content и date. Три этих закрытых поля аннотируются с помощью @Persistent, чтобы DataNucleus сохраняла их как свойства объектов в хранилище данных App Engine.
Этот класс определяет получатели и установщики свойств. Они используются только приложением и не нужны для JDO.
Этот класс также определяет поле id, Long аннотированное и как @Persistent, и как @PrimaryKey. Хранилище данных App Engine знает о ключах элементов и может представлять ключ объекту несколькими способами. В данном случае ключ поля – длинное целое число, которое устанавливается автоматически при сохранении объекта как уникальный численный идентификатор.
Дополнительные сведения об аннотациях JDO см. в статье Определение классов данных.
Все запросы, использующие хранилище данных, создают новые экземпляры класса PersistenceManager. Это делается с помощью экземпляра класса PersistenceManagerFactory.
Экземпляр PersistenceManagerFactory инициализируется некоторое время. К счастью, для приложения нужен только один экземпляр, который можно хранить в статической переменной, которой пользуются многие запросы и классы. Это нетрудно сделать, создав отдельный класс-оболочку для статического экземпляра.
Создайте новый класс PMF в пакете guestbook (файл PMF.java в каталоге src/guestbook/) и поместите туда следующее содержание:
package guestbook;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
public final class PMF {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");
private PMF() {}
public static PersistenceManagerFactory get() {
return pmfInstance;
}
}
После настройки DataNucleus и класса Greeting логика обработки форм может хранить новые приветствия в хранилище данных.
Измените src/guestbook/SignGuestbookServlet.java указанным образом, чтобы он выглядел так:
package guestbook;
import java.io.IOException;
import java.util.Date;
import java.util.logging.Logger;
import javax.jdo.PersistenceManager;
import javax.servlet.http.*;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import guestbook.Greeting;
import guestbook.PMF;
public class SignGuestbookServlet extends HttpServlet {
private static final Logger log = Logger.getLogger(SignGuestbookServlet.class.getName());
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
String content = req.getParameter("content");
Date date = new Date();
Greeting greeting = new Greeting(user, content, date);
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.makePersistent(greeting);
} finally {
pm.close();
}
resp.sendRedirect("/intl/ru/guestbook.jsp");
}
}
Этот код создает новый экземпляр Greeting, вызывая конструктор. Для сохранения экземпляра в хранилище данных он создает PersistenceManager с помощью PersistenceManagerFactory и передает экземпляр методу makePersistent() PersistenceManager. Там его принимают аннотации и улучшение байтового кода. После возврата makePersistent() новый объект сохраняется в хранилище данных.
Стандарт JDO определяет механизм опрашивания постоянных объектов под названием JDOQL. JDOQL можно использовать для выполнения запросов элементов в хранилище данных App Engine и возврата результатов в виде объектов, улучшенных в отношении JDO.
Для этого примера мы все упростим, записав код запроса непосредственно в guestbook.jsp. Для большего приложения может быть нужно передать логику запроса в другой класс.
Измените war/guestbook.jsp и добавьте указанные строки, чтобы получить такой результат:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.List" %>
<%@ page import="javax.jdo.PersistenceManager" %>
<%@ page import="com.google.appengine.api.users.User" %>
<%@ page import="com.google.appengine.api.users.UserService" %>
<%@ page import="com.google.appengine.api.users.UserServiceFactory" %>
<%@ page import="guestbook.Greeting" %>
<%@ page import="guestbook.PMF" %>
<html>
<body>
<%
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
if (user != null) {
%>
<p>Hello, <%= user.getNickname() %>! (You can
<a href="<%= userService.createLogoutURL(request.getRequestURI()) %>">sign out</a>.)</p>
<%
} else {
%>
<p>Hello!
<a href="<%= userService.createLoginURL(request.getRequestURI()) %>">Sign in</a>
to include your name with greetings you post.</p>
<%
}
%>
<%
PersistenceManager pm = PMF.get().getPersistenceManager();
String query = "select from " + Greeting.class.getName();
List<Greeting> greetings = (List<Greeting>) pm.newQuery(query).execute();
if (greetings.isEmpty()) {
%>
<p>The guestbook has no messages.</p>
<%
} else {
for (Greeting g : greetings) {
if (g.getAuthor() == null) {
%>
<p>An anonymous person wrote:</p>
<%
} else {
%>
<p><b><%= g.getAuthor().getNickname() %></b> wrote:</p>
<%
}
%>
<blockquote><%= g.getContent() %></blockquote>
<%
}
}
pm.close();
%>
<form action="/sign" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Post Greeting" /></div>
</form>
</body>
</html>
Для подготовки запроса вызывается метод newQuery() экземпляра PersistenceManager с текстом запроса в виде строки. Метод возвращает объект запроса. Метод объекта запроса execute() выполняет запрос и возвращает List<> объектов результатов соответствующего типа. Строка запроса должна включать полное название класса, который надо опрашивать, включая название пакета.
Снова соберите проект и перезапустите сервер. Зайдите на http://localhost:8080/. Введите приветствие и отправьте его. Приветствие появляется над формой. Введите еще одно приветствие и отправьте его. Отображаются оба приветствия. Попробуйте выйти и войти с помощью ссылок и попробуйте отправлять сообщения при выполненном входа и без такового.
Совет. В настоящем приложении стоит переводить символы HTML, отображая отправленное пользователями содержание, такое как приветствия в этом приложении. Библиотека стандартных тегов страниц JavaServer (JSTL) включает процедуры для этого. App Engine включает JSTL (и другие связанные с JSP JAR выполнения), поэтому включать их в приложение не нужно. Найдите функцию escapeXml в библиотеке тегов http://java.sun.com/jsp/jstl/functions. Дополнительные сведения см. в Учебнике Sun J2EE 1.4.
Наша гостевая книга отображает все сообщения, отправленные в систему. Она отображает их в порядке создания. Если в ней будет много сообщений, будет полезно отображать только свежие сообщения, показывая последнее сверху. Это можно сделать, изменив запрос к хранилищу данных.
Запрос выполняется с интерфейсом JDO с помощью JDOQL, сходного с SQL языка запросов для получения объектов данных. На нашей странице JSP следующая строка определяет строку запроса JDOQL:
String query = "select from " + Greeting.class.getName();
Другими словами, строка запроса JDOQL – это следующее:
select from guestbook.Greeting
Этот запрос спрашивает у базы данных все сохраненные экземпляры класса Greeting.
Запрос может фильтровать результаты, включая критерии, которым должны соответствовать свойства объекта, чтобы он попал в результаты. Например, для получения только объектов Greeting, свойство author которых – alfred@example.com, следует использовать такой запрос:
select from guestbook.Greeting where author == 'alfred@example.com'
Запрос может указывать (в терминах значений свойств) порядок, в котором следует вернуть результаты. Для получения всех объектов Greeting в порядке, обратном отправке (с самых новых к самым старым) следует использовать следующий запрос:
select from guestbook.Greeting order by date desc
Запрос может ограничивать результаты определенным диапазоном. Для получения 5 последних приветствий следует использовать order by и range вместе, следующим образом:
select from guestbook.Greeting order by date desc range 0,5
Сделаем это для приложения гостевой книги. В guestbook.jsp, замените определение query следующим:
String query = "select from " + Greeting.class.getName() + " order by date desc range 0,5";
Отправьте в гостевую книгу более 5 сообщений. Отображаются только последние 5, в обратном хронологическом порядке.
Дополнительные сведения о запросах и JDOQL см. в статье Запросы и индексы.
Любое веб-приложение возвращает динамически созданный HTML из кода приложения посредством шаблонов или другого механизма. Большинство веб-приложений также должны выводить статическое содержание, например изображения, таблицы CSS-стилей и файлы JavaScript. Для повышения эффективности App Engine обрабатывает статические файлы иначе, чем код и данные приложения. Создадим таблицу стилей CSS для этого приложения в виде статического файла.
Переходите к разделу Использование статических файлов.