Google Cloud Endpoints на Java: Руководство. ч. 1
Google Cloud Endpoints — это надстройка над Google App Engine (GAE) для создания API для веб и мобильных приложений, делающая разработку проще и включающую в себя «из коробки» защиту от DoS-атак, OAuth 2.0 аторизацию, веб-интерфейс для тестирования API, SSL, атоматическую масштабируемость (сайт не упадет под хабра-эффектом), а также возможность использования сервисов доступных в Google App Engine (отсылка и прием электронной почты и XMPP-сообщений, загрузка данных из Интернет (URL Fetch service), задачи по расписанию (Task Queues and Scheduled Tasks) и др.)
GAE бесплатен в рамках начальных квот, которые позволяют попробовать и протестировать сервис, и также обеспечить бесплатное функционирование веб-сайта не имеющего больших нагрузок. При исчерпании квот сервис становиться платным.
Идея сервиса в том, что он делает всю или большую часть работы системного администратора, плюс некоторую часть работы программиста. Этот сервис может быть интересен стартапам, так как позволяет малыми силами и в котроткие сроки запустить рабочий проект.
Фреймворк Objectify предоставляет удобные стредства для работы со базой данных встроенной в GAE, а модуль angular-google-gapi для подключения веб-приложения на AngularJS c авторизацией пользователей.
Под катом много картинок и текста, и предполагается, что читатель знаком с Java Servlets.
Регистрация
Для начала понадобится создать проект на GAE. Для этого потребуется учетная запись google. Можно воспользоваться существующей или создать новую. Политика Google допускает создание нескольких учетных записей для одного лица. Google предупреждает, когда приложение запрашивает данные пользователя (OAuth consent screen), то в числе данных о приложении может передаваться информация о разработчике, в том числе его адрес электронной почты, и это не любой адрес электронной почты, а именно учетной записи @ gmail.com под которой работают c GAE, так что возможно стоит заводить отдельные учетные записи для разных проектов, благо тот же Google Chrome позволяет удобно между ними переключаться.
Переходим в консоль разработчика Google: console.developers.google.com и логинимся используя выбранную учетную запись Google, нажимаем «Create an empty project», выбираем имя проекта и ID проекта. Проект будет доступен по адресу http://
ID должно быть уникальным. При этом если
После того как проект создан, он будет доступен в консоли разработчика:
Для данного руководства мы создадим два проекта в консоли разработчика Google: hello-habrahabr-api.appspot.com, где будет API (собственно Google Cloud Endpoints), и hello-habrahabr-webapp.appspot.com, на котором разместим веб-клиент. Я делаю это для наглядности разделения фронэнда и бэкенда, можно было бы создавать на одном домене.
В консоли разработчика нашего Cloud Endpoints проекта hello-habrahabr-api.appspot.com в меню выбираем APIs & auth > Credentials > Add Credentials > OAuth 2.0 Client ID > Configure consent screen.
В настойках автоматически вставляется адрес электронной почты разработчика, также указываем имя приложения (Product name) (обязательно), и (опционально) адрес веб-страницы, логотип и другие данные.
В меню выбираем APIs & auth > Credentials > Add Credentials > OAuth 2.0 Client ID и указываем тип приложения для которого создаем client ID. В этом руководстве мы будем рассматривать создание веб-приложения, поэтому выбираем Web application, и нажимаем «Create».
— имя веб-приложения (любое)
— Authorized redirect URIs: не используем
Нажимаем «Create», получаем «client ID» и «client secret» — они нам понадобятся для веб-приложения.
Подготовка и настройка рабочей среды.
1. Java 7 (Java 8 пока не поддерживается GAE)
В Linux (последняя версия 3.3.3):
Для Ubuntu/Debian (в депозитариях сейчас версия 3.0.5):
Проверяем версию Maven и Java:
3. IDE или редактор кода — по вкусу. Eclipse и IntelliJ IDEA Ultimate имеют плагины для работы с GAE, но тестирование оффлайн на своей машине для Cloud Endpoints все равно работать не будет (хотя в документации указано что должно), поэтому для разделения кода в разработке и работающей системы нужно либо использовать раздельные проекты, либо пользоваться предоставляемой GAE возможностью работы с разными версиями проекта.
Вместо GAE-плагинов проще и удобнее (имхо) использовать Maven в командной строке, так что вполне подойдет IntelliJ IDEA Community Edtion – там есть встроенный терминал, и это все что нужно. В Eclipse можно установить TM Terminal.
4. Google App Engine SDK. Если необходимо — можно скачать тут.
Но при использовании Maven, он скачает Google App Engine SDK автоматически.
Создаем основу бэкенда
Переходим в директорию в которой мы будем создавать директорию проекта. Избегайте директорий с нестандартными символами в наименовании и очень длинным путем, это может вызвать ошибки в Maven.
Для создания заготовки проекта используем Maven и Endpoints Skeleton Archetype:
Выбираем версию (по умолчанию — последняя).
Define value for property ‘groupId’: — вводим groupId, это уникальный идентификатор нашего приложения, следуя Maven Guide to naming conventions on groupId, artifactId and version — как правило используется имя домена в обратном порядке, но с учетом допустимых символов package name rules — то есть в моем случае вместо com.appspot.hello-habrahabr-api надо ввести com.appspot.hellohabrahabrapi или com.appspot.hello_habrahabr_api (я выбрал последнее).
Define value for property ‘version’ — вводим номер версии, для GAE номер версий должен быть без точек, например не 1.0.3, а 1-0-3. Это связанно с тем, что на GAE доступны версии проекта по адресу вида <номер версии>.
Также в GAE используется формат <номер instance>.<нормер версии>.
По этой причине вариант предлагаемый Maven по умолчанию — 1.0-SNAPSHOT — лучше заменять формат подходящий по стандартам GAE.
Define value for property ‘package’ — обычно принимаем предложенное по умолчанию (будет соотвествовать groupId).
Подтверждаем выбранные настройки: Y (yes, по умолчанию).
Если все сделано правильно, получаем сообщение об успешной сборке:
Maven создаст папку имя которой будет соотвествовать artifactId с файлами проекта. Папка имеет следующую структуру:
pom.xml
Для начала нам следует отредактировать pom.xml который содержит настройки Maven для нашего проекта. В коде:
прописываем ту версию Maven, которая у нас установлена, в моем случае 3.1.0 я заменяю на 3.3.3.
src/main/webapp/WEB-INF/appengine-web.xml
Если сборка была неуспешной, ищем ошибки в настройках. Если — успешной, загружаем проект на GAE:
При первом запуске Maven скачает Google App Engine SDK, и сохранит в
/.m2/repository/com/google/appengine/appengine-java-sdk/ <номер версии>(на Линукс).
Также при первом запуске из консоли нужно будет залогиниться в GAE, в окне браузера откроется диалог:
При нажатии на кнопку, в новом окне появиться код, который нужно скопировать и ввести в консоли.
Токены аутентификации пользователя храняться в
/.appcfg_oauth2_tokens_java. Если при попытке входа вы получаете сообщение 404 Not Found This application does not exist (app_id=u’
Если команда mvn appengine:update выполнена успешно («BUILD SUCCESS») значит первоначальные настройки сделаны успешно.
Наблюдался баг при загрузке в версии 1.9.21, который можно было исправить следующим образом: в pom.xml во всех местна заменяем appengine.version на appengine.sdk.version (см. code.google.com/p/googleappengine/issues/detail?id=12009) В текущей верси (1.9.27) баг не проявляется, но знать на всякий случай не помешает.
В версии 1.9.27 при загрузке можно получить сообщение:
Несмотря на это предупреждение, загрузка должна проходить успешно.
Настраиваем Git на GAE
Сначала инициализируем git репозиторий в директории проекта:
Переходим в консоль разработчика: console.developers.google.com/project, и выбираем наш проект.
Выбираем в меню: Source code > Browse. Сайт предложит создать репозиторий Git. Жмем «Get started»
Можно скопировать код с существующего репозитория на Github или Bitbucket, загрузить (push) код со своего копьютера, или клонировать (clone) код проекта с GAE на свой компьютер. Выберем «push». Получаем инструкцию:
1.Установить Google Cloud SDK
Инструкции по установке. Для Linux / Mac OS X:
В будущем настройки можно поменять командой: gcloud config.
Стереть настройки Google Cloud SDK:
gcloud config unset account — стереть учетную запись в настройках
gcloud config unset project — стереть имя проекта в настройках
2. Производим аутентификацию
На Linux / Mac OS X:
3. Добавляем удаленный git репозиторий GAE
4. Выгружаем в удаленный репозиторий
Чтобы клонировать репозиторий из облака GAE на локальную машину, нам потребуется:
Установить Google Cloud SDK как указано выше.
Клонировать репозиторий на локальную машину (инструкция на сайте предлагающая для этого gcloud init
Переходим в созданную директорию, пишем и коммитим код в локальном репозитории, и
Для атоматизации работы можно использовать следующий скрипт (что-то вроде commit.push.build.and.upload.sh):
Если приходится переключаться между проектами, в директорию проекта стоит разместить скрипт для быстрой и удобной смены настроек (set.account.sh):
Приступаем к написанию API
Запускаем любимый редактор или IDE.
Прежде всего рассмотрим как выглядит WEB-INF/web.xml. В нашем случае:
Обратим внимание, что все запросы к API направляются по адресу: /_ah/spi/* и обрабатываются сервлетом com.google.api.server.spi.SystemServiceServlet (SystemServiceServlet).
Одна из основных «фишек» Cloud Endpoints — веб-интерфейс для тестирования API (API Explorer) достутпен по адресу <проект ID>.appspot.com/_ah/api/explorer.
Для моделирования данных принимаемых и выдаваемых API используются JavaBean, т.е. классы, отвечающие требованиям:
* публичный (public) конструктор без параметров. В данном случае конструктор должен быть указан в явном виде, хотя в примерах на сайте Google это упускают, но без конструктора на практике не работает.
* все свойства класса приватные, доступ через get/set (для boolean getter также должен начинаться с get, а не так как сгенерирует IDE).
* класс должен быть сериализуем (в явном виде можно не указывать).
Создадим два класса:
Теперь пишем класс для первого API-метода. Редактируем YourFirstApi.java, и вставим туда следующий код:
Веб-интерфейс к API (APIs Explorer)
Теперь деплоим (mvn clean install && mvn appengine:update) и открываем в веб-браузере адрес https://<проект ID>.appspot.com/_ah/api/explorer, в моем случае hello-habrahabr-api.appspot.com/_ah/api/explorer.
Кликаем на название нашего API (если мы создадим несколько классов в аннотацией Api — из будет несколько) и видим методы, содержащиеся в этом API (методы класса с аннтотацией @ApiMethod):
Кликнув по полю «Request body» мы можем заполнить данные запроса получаемого api-методом:
Далее мы можем выбрать «Autorize and execute» — и тогда нам потребуется пройти авторизацию ипользуя учетную запись Google (@ gmail.com) либо выбрать «Execute without OAuth», поскольку наше API пока никак не использует авторизацию, мы увидим одинаковые результаты с авторизацией и без:
Logging
Логи доступны по адресу: console.developers.google.com/project/<проект ID>/logs.
Настройки в файле /src/main/webapp/WEB-INF/logging.properties и /src/main/webapp/WEB-INF/appengine-web.xml.
Для того чтобы наш класс выдавал сообщения в лог, нужно классе API:
Авторизация OAuth 2.0 используя учетную запись Google (@ gmail.com)
В Constants.java добавим:
Это необходимо для тестирования OAuth-защищенных API-методов в APIs Explorer.
Наш Constants.java будет выглядеть следующим образом:
Теперь создадим новый класс:
И пропишем его в init-param сервлета SystemServiceServlet в web.xml:
Деплоим проект, и смотрим API Explorer:
Мы видим новое API в списке, кликнув по нему видим список его методов.
Кликаем на название метода:
Теперь, если мы нажмем «Execute without OAuth» получим Exception:
Если кликаем «Autorize and execute» — нужно залогиниться используя учетную запись Google. В Response соотвественно получим email, nickname и userId (уникальный номер пользователя Google).
Объект класса com.google.appengine.api.users.User предоставляется GAE и содержит информацию о текущем пользователе, если пользователь не авторизован, соотвественно null. Таким образом мы можем проводить авторизацию используя логин-пароль учетной записи Google.
Как уже упоминалось на Хабре (Иногда лучше меньше — почему только Google-авторизация?, Юзабилити форм авторизации) проект вообще может обойтись без собственной обработки логинов и паролей.
На мой взгляд, это правильный подход, в первую очередь с точки зрения безопасности. Естественно, мы можем делать «регистрацию» на сайте после ввода дополнительной информации, платежа, и т.п.
Создание фронтэнда на AngularJS рассмотрим в следующей статье.
Endpoint java что это
A Web service endpoint.
Endpoints are created using the static methods defined in this class. An endpoint is always tied to one Binding and one implementor, both set at endpoint creation time.
An endpoint is either in a published or an unpublished state. The publish methods can be used to start publishing an endpoint, at which point it starts accepting incoming requests. Conversely, the stop method can be used to stop accepting incoming requests and take the endpoint down. Once stopped, an endpoint cannot be published again.
An Executor may be set on the endpoint in order to gain better control over the threads used to dispatch incoming requests. For instance, thread pooling with certain parameters can be enabled by creating a ThreadPoolExecutor and registering it with the endpoint.
An endpoint may have a list of metadata documents, such as WSDL and XMLSchema documents, bound to it. At publishing time, the JAX-WS implementation will try to reuse as much of that metadata as possible instead of generating new one based on the annotations present on the implementor.
| Field Summary | |
|---|---|
| static String | WSDL_PORT Standard property: name of WSDL port. |
| static String | WSDL_SERVICE Standard property: name of WSDL service. |
| Constructor Summary | |
|---|---|
| Endpoint () | |
| Method Summary | |
|---|---|
| static Endpoint | create (Object implementor) Creates an endpoint with the specified implementor object. |
| static Endpoint | create (String bindingId, Object implementor) Creates an endpoint with the specified binding type and implementor object. |
| abstract Binding | getBinding () Returns the binding for this endpoint. |
| abstract Executor | getExecutor () Returns the executor for this Endpoint instance. |
| abstract Object | getImplementor () Returns the implementation object for this endpoint. |
| abstract List | getMetadata () Returns a list of metadata documents for the service. |
| abstract Map | getProperties () Returns the property bag for this Endpoint instance. |
| abstract boolean | isPublished () Returns true if the endpoint is in the published state. |
| abstract void | publish (Object serverContext) Publishes this endpoint at the provided server context. |
| abstract void | publish (String address) Publishes this endpoint at the given address. |
| static Endpoint | publish (String address, Object implementor) Creates and publishes an endpoint for the specified implementor object at the given address. |
| abstract void | setExecutor (Executor executor) Sets the executor for this Endpoint instance. |
| abstract void | setMetadata (List metadata) Sets the metadata for this endpoint. |
| abstract void | setProperties (Map properties) Sets the property bag for this Endpoint instance. |
| abstract void | stop () Stops publishing this endpoint. |
| Methods inherited from class java.lang.Object |
|---|
| clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
WSDL_SERVICE
WSDL_PORT
See Also: Constant Field Values
| Constructor Detail |
|---|
Endpoint
create
The newly created endpoint may be published by calling one of the javax.xml.ws.Endpoint#publish(String) and javax.xml.ws.Endpoint#publish(Object) methods.
create
The newly created endpoint may be published by calling one of the javax.xml.ws.Endpoint#publish(String) and javax.xml.ws.Endpoint#publish(Object) methods.
getBinding
Returns: The binding for this endpoint
getImplementor
Returns: The implementor for this endpoint
publish
publish
The necessary server infrastructure will be created and configured by the JAX-WS implementation using some default configuration. In order to get more control over the server configuration, please use the javax.xml.ws.Endpoint#create(String,Object) and javax.xml.ws.Endpoint#publish(Object) method instead.
publish
isPublished
Returns: true if the endpoint is in the published state.
getMetadata
Returns: List A list of metadata documents for the service
setMetadata
getExecutor
Returns: The java.util.concurrent.Executor to be used to dispatch a request. See Also: Executor
setExecutor
getProperties
Returns: Map The property bag associated with this instance.
setProperties
Copyright 2007 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms.
JavaEE. Простое создание веб-сервисов с JAX-WS.
JAX-WS (Java API for XML Web Services) – это спецификация из мира JavaEE, посвященная созданию веб-сервисов на языке Java.
Данной спецификации посвящен документ JSR 224. При описании веб-сервисов JAX-WS возможно использование одного из двух подходов:
contract first – описание веб-сервиса осуществляется на специальном языке – WSDL (Web Service Descrption Language). В итоге мы получаем XML- документ, описывающий наш веб-сервис.. По данному документу JAX-WS фреймворк может сгенерировать обычные Java- классы, в виде proxy к удаленным сервисам.
contract last – программист как обычно проектирует Java- классы с нуля, самостоятельно помечая классы и методы, представляющие Web- сервис, специальными аннотациями и уже по ним генерируется WSDL-документ.
Какой из подходов выбрать — дело программиста. Иногда, при большом количестве деталей и желании держать под контролем описание сервиса, имеет смысл сначала создавать WSDL-описание. Если же сервис простой, то гораздо легче будет написать Java- классы самостоятельно, чем вдаваться в детали WSDL, особенно если вам это в ближайшее время больше не понадобится.
Если вы не знакомы с JAXB, можете обратиться к этой статье.
Contract Last.
В данном примере мы не будем описывать WSDL вручную, а попробуем создать наш веб-сервис «по старинке», путем написания обычного Java- кода. А генерацию документа доверим JAX-WS- фреймворку.
Итак, для создания веб-сервиса с использованием подхода с ontract last нам может пригодиться следующий набор аннотаций, описанный в спецификации JSR 181. Среди них есть:
@WebService – указывает на то, что данный класс является веб-сервисом JAX-WS.
@WebMethod – данный метод может быть вызван удаленно и является одним из методов веб-сервиса.
@WebResult – позволяет настроить соответствие возвращаемого результата в WSDL и XML.
Первым делом добавим зависимости в наш проект. Я использую для сборки проектов Maven.
Так как JAX-WS просто спецификация, то нам нужна и реализация. Самыми популярными реализациями JAX-WS являются такие фреймворки как: Apache CXF, Axis2, Metro.
SOAP Web-сервис средствами Spring-WS
Когда-то поставили передо мной задачу начать разработку Web-сервисов и дали мне сорцы простейшего проекта без каких-либо объяснений. Проект, конечно же, не запускался. Что такое Spring и как он работает, я тоже представления не имел. Адекватных статей по разработке Web-сервисов средствами Spring ни русскоязычных, ни англоязычных я тоже не смог найти. Пришлось разбираться во всем самому, оказалось все не так страшно.
И вот недавно я решил посмотреть, какие новые возможности добавились в Spring с тех пор, и обновить старые сервисы, что в результате и сподвигло меня на написание данной статьи.
Данная статья является руководством по разработке простейшего Web-сервиса, использующего SOAP-протокол, средствами Spring-WS.
И так, писать будем простейший сервис, принимающий имя пользователя и отправляющий приветствие и текущее время на сервере.
Что же нам потребуется?
Подготовка к работе
Создаем новый проект Web-приложения. В Eclipse это: «File => New => Dynamic Web Project».
Я назвал проект: HelloService.
Далее копируем библиотеки из Spring, XMLBean, wsdl4j, commons-logging в каталог проекта WEB-INF/lib.
При желании можете добавить их к библиотекам сервера, чтобы не таскать их с каждым приложением.
Создание WSDL-схемы
По сути WSDL-схема предназначена для описания сервиса.
Вручную создавать её мы, конечно же, не будем. Схема будет сгенерирована автоматически средствами Spring’а, но об этом позднее.
Определяем входные и выходные данные
Создаем описание входных и выходных данных
В каталоге WEB-INF создаем файл HelloService.xsd. Данный файл нужен будет для генерации WSDL-схемы и создания соответствующих Java-классов.
Текст файла:
Атрибут targetNamespace – используемое пространство имен. Т.е. все созданные объекты будут располагаться в пакете org.example.helloService.
Элементы ServiceRequest и ServiceResponse описывают соответственно входные и выходные данные (запрос/ответ).
Атрибуты minOccurs и maxOccurs определяют количество повторений данного компонента в пределах одного элемента. Если эти параметры не указывать, то по умолчанию они считаются равными 1. Для необязательного компонента необходимо указать minOccurs=0. При неограниченном количестве компонент: maxOccurs=unbounded.
Подробнее о XML-схемах можно прочитать здесь.
Создаем JavaBeans
На основании созданной схемы будем создавать Java классы. Для этого создаем файл build.xml:
Параметр WS_HOME должен указывать на каталог, где располагается XMLBeans.
HelloService.xsd – путь к созданной схеме.
lib\helloservice.jar – создаваемая java-библиотека.
Реализация сервиса
Создаем интерфейс и класс сервиса
Интерфейс сервиса: HelloService.java:
Реализация сервиса: HelloServiceImpl.java:
Данный код, я думаю, не нуждается в комментариях. Единственное, что у людей, не сталкивающихся ранее со Spring’ом, может вызвать вопросы, так это аннотация @ Service. Но об этом же расскажу чуть позже.
Endpoint
Endpoint – класс, который будет отвечать за обработку входящих запросов (своего рода точка входа).
Создаем файл HelloServiceEndpoint.java:
Что же здесь сделано?
Аннотация @Endpoint как раз и определяет, что данный класс будет обрабатывать входящие запросы.
namespaceUri – то же пространство имен, что и указывалось при создании xml-схемы.
Теперь вернемся немного назад и вспомним про аннотацию @ Service. Если не вдаваться в подробности, чтобы не перегружать читателя лишней информацией, то эта аннотацию говорит Spring’у создать соответствующий объект. А аннотация @Autowired служит для инъекции (автоматической подстановки) соответствующего объекта. Конечно же при построении простых приложений в использовании данных аннотаций отсутствует смысл, но я решил все-такие не исключать их в данном примере.
И так, идем далее.
Аннотация @PayloadRoot перед методом определяет, при получении какого запроса будет вызван данный метод. В нашем случае, это «ServiceRequest».
В остальном опять же все должно быть ясно. Обратите внимание, что ServiceRequest, ServiceResponse и т.д. – это как раз те классы, которые были созданы на основе нашей xml-схемы.
Spring-конфигурация сервиса
Вот и близится уже завершение.
Создаем файл service-ws-servlet.xml.
sws:annotation-driven – говорит как раз о том, что в данном проекте используются аннотации.
А context:component-scan указывает на пакет, в котором будет производится поиск аннотаций, при этом поиск производится и в подпакетах.
Два последующих бина всегда будут неизменны. Суть их заключается в приеме и преобразовании запроса из Xml в Java-объект и дальнейшего обратного преобразования.
sws:dynamic-wsdl отвечает за автоматическую генерацию WSDL-документа на основе созданной Xml-схемы.
location указывает на путь к схеме.
locationUri – адрес (относительно контейнера), по которому будет доступна WSDL-схема.
В моем случае WSDL доступен по следующему адресу:
localhost/HelloService/HelloService.wsdl
Дескриптор развертывания
Ну и, наконец, последнее.
В каталоге WEB-INF изменяем или создаем файл web.xml.
Данный файл описывать уже не буду, большинство и так должны знать. Для несложных проектов он по сути не должен изменяться. Стоит отметить только, что имя сервлета(servlet-name) должно соответствовать имени файла Spring-конфигурации сервиса service-ws-servlet.xml.
Ну и далее деплоим приложение на сервер.
На этом создание сервиса завершено. Если ничего не пропустили, то сервис должен функционировать.
Проверка работоспособности
Самым первым признаком корректной работы является созданная WSDL-схема.
Для проверки просто переходим по адресу этой схемы (http://localhost/HelloService/HelloService.wsdl) и смотрим: там должен отобразиться xml-файл. Если ничего не отобразилось или какая ошибка появилась, перечитываем внимательно всю статью и ищем, что сделали не так.
Для дальнейшей проверки нам потребуется soapUI (у меня версия 3.0.1).
Устанавливаем и запускаем его.
Создаем новый проект: File => New soapUI Project. В поле Initial WSDL/WADL вставляем ссылку на WSDL-схему (http://localhost/HelloService/HelloService.wsdl).
В созданном проекте открываем необходимый запрос.
В поле Name вбиваем имя и жмем на кнопку «Send request»
В результате получаем ответ от сервера с приветствием и текущим временем.
Если что-то пошло не так, то опять перечитываем данную статью.



