Содержание


Пример построения переносимого Java-приложения, интегрирующего веб-сервисы

Comments

Я решила продемонстрировать высокий уровень способности к взаимодействию и переносимости, который может быть достигнут при построении и развертывании приложения в облаке. С этой целью я создала небольшое игрушечное приложение для путешественников.

Мое приложение, которое состоит из двух частей, на основании предпочтений пользователя, хранящихся в его профиле, представляет ему карту, демонстрирующую свободное жилье под наем в зоне его нахождения. Первая часть приложения управляет предпочтениями пользователя с помощью сервиса MongoDB, доступного в среде BlueMix, и представляет результирующий сервис внешнему миру при посредстве API-интерфейса. Вторая часть приложения интегрирует первую часть с внешними сервисами в рамках веб-приложения.

Облачное приложение должно быть масштабируемым и переносимым и должно легко интегрироваться с внутренними сервисами. Оно не должно требовать много усилий для инициализации и управления на всем протяжении своего жизненного цикла

Запустить приложениеПолучить программный код

Примечание.

  • После нажатия на Run the app вы можете войти в систему с любым идентификатором и паролем по своему желанию.
  • Чтобы клонировать код этого упражнения, после нажатия на Get the code нажмите кнопку EDIT CODE в правом верхнем углу (введите свои учетные данные DevOps Services, если вы еще не вошли в систему), а затем нажмите кнопку FORK в меню с целью создания нового проекта. В качестве альтернативного варианта вы можете экспортировать этот код. Для этого выберите корневую папку, затем выберите File > Export в левой навигационной панели.

Что вам потребуется для создания подобного приложения

  • Базовые знания MVC-среды Spring
  • Пакет Spring Tool Suite (или Eclipse IDE) с установленным плагином Cloud Foundry
  • MongoDB— база данных типа NoSQL
  • Учетная запись Google Maps API (для проведения тестирования)
  • Учетная запись Expedia Developer API (для проведения тестирования)
  • jquery-ui-map— плагин Google Maps V3 для jQuery (упрощает работу с Google Maps API)

Шаг 1. Создание облачного приложения

Я создала и развернула этот пример на платформе Bluemix. Для этого примера я выбрала Java со средой Spring Framework. Чтобы создать приложение, достаточно перейти в среду BlueMix и выбрать тип приложения (Java автономное, Java Web, Ruby и т.д.). В нашем случае приложение имеет тип Java Web.

Screenshot creating an application using the BlueMix site
Screenshot creating an application using the BlueMix site

Шаг 2. Установка и использование инструмента командной строки cf

Управлять этим приложением можно с помощью веб-интерфейса Bluemix или с помощью интерфейса командной строки (CLI), предоставляемого проектом Cloud Foundry. Для этого примера я выбрала интерфейс командной строки cf. Командная строка позволяет развертывать приложение, ассоциировать приложение с сервисом, управлять приложением (старт и останов) и т. д. Загрузите CLI-интерфейс cf с веб-сайта GitHub и запустите процедуру установки. Результатом выполнения этой процедуры является исполняемый файл cf.exe. Сначала вы должны настроить целевую конечную точку API-интерфейса, а затем войти в систему.

Screenshot showing logging in to the API endpoint.
Screenshot showing logging in to the API endpoint.

Теперь вы можете получать списки приложений, сервисов и связанных сервисов.

Шаг 3. Подготовка среды разработки

В этом примере используется MVC-среда Spring Framework, точнее пакет Spring Tool Suite с плагином Cloud Foundry. В частности, задействованы следующие инструменты и технологии:

  1. Spring 3.1.1
  2. JDK 7
  3. Spring Tool Suite 3.4.0+ Cloud Foundry Integration for Eclipse 1.5.1

Создавая проект Java Web Project для развертывания на платформе Cloud Foundry, необходимо добавить в этот проект специфику Cloud Foundry. С этой целью создается файл manifest.yml, который описывает приложение и его потребности в ресурсах для среды исполнения Cloud Foundry.

Наше приложение будет состоять из двух частей.

  • Первая часть имеет имя UserService. Эта часть представляет API-интерфейс для управления информацией пользователя. Она использует внутреннюю облачную платформу MongoDB (популярная база данных типа NoSQL) для реализации персистентности данных.
  • Вторая часть имеет имя MyVacations. Она позволяет зарегистрированному пользователю искать доступные отели с использованием определенных параметров. Часть (приложение) UserService предоставляет значения некоторых из этих поисковых параметров. Сервис Expedia Services предоставляет список отелей и информацию о них. Сервис Google Maps API Web Services отображает этот список отелей на карте.
Simplified deployment model of applications described in the sample
Simplified deployment model of applications described in the sample

Приложение UserService показывает, как хранить информацию о пользователях в центральном местоположении с помощью сервиса MongoDB.

Функции приложения UserService:

  1. Функция журналирования — UserService получает имя/пароль пользователя и ищет этого пользователя в базе данных. В случае нахождения такого пользователя UserService возвращает сведения о нем, в противном случае — создает новую запись.
  2. Функция профилирования — UserService получает имя пользователя и ищет информацию об этом пользователе (предпочтительное местоположение, количество взрослых, количество детей) и возвращает их клиенту.

Шаг 4. Связывание облачного сервиса (MongoDB)

Чтобы использовать сервис MongoDB, сначала требуется создать экземпляр этого сервиса.

  1. Подключитесь к Bluemix и в представлении Dashboard выберите Add a Service. BlueMix dashboard of applications
    BlueMix dashboard of applications
  2. Из списка доступных сервисов выберите MongoDB и создайте экземпляр этого сервиса. Create the MongoDB istance service for UserService application.
    Create the MongoDB istance service for UserService application.

В качестве альтернативного варианта можно использовать инструмент командной строки:

cf create-service mongodb 100 mongodb_ser1
(USAGE:    cf create-service SERVICE PLAN SERVICE_INSTANCE)

cf bind-service UserService mongodb_ser1
(USAGE:    cf bind-service APP SERVICE_INSTANCE)

Теперь экземпляр сервиса MongoDB готов и связан с приложением UserService.

Шаг 5. Использование сервиса MongoDB в приложении

После создания сервиса и связывания его с вашим приложением, его конфигурация добавляется в переменную VCAP_SERVICESкак в переменную среды "только для чтения". Эта переменная содержит информацию, которую вы можете использовать в коде для соединения с вашими сервисами. В этом случае ваш код выглядит следующим образом.

{
  "mongodb-2.2": [
      {
         "name": "mongodb-ser1",
         "label": "mongodb-2.2",
         "plan": "100",
         "credentials": {
            "hostname": "10.0.116.106",
            "host": "10.0.116.106",
            "port": 10192,
            "username": "46c538a6-e6e1-4d02-8132-77b0a4b2dc1c",
            "password": "0ceea0ea-5548-46ad-9b09-1002683aeca7",
            "name": "946dc87b-b455-4d12-b977-1b1ee22f1ade",
            "db": "db",
            "url": "mongodb://46c538a6-e6e1-4d02-8132-77b0a4b2dc1c:
0ceea0ea-5548-46ad-9b09-1002683aeca7@10.0.116.106:10192/db"
         }
      }
   ]
}

Чтобы соединиться с экземпляром сервиса MongoDB с использованием переменной VCAP_SERVICES, вы извлекаете URL-адрес JsonNode. Обратите внимание, что этот URL-адрес содержит все необходимые параметры для соединения с базой данных (учетные данные пользователя, имя хоста, порт, имя базы данных).

private static String getUrlConnection() {
			
	String env = System.getenv("VCAP_SERVICES");
	ObjectMapper mapper = new ObjectMapper();
	try {
		JsonNode node = mapper.readTree(env);
		Iterator<JsonNode> dbNode = node.get("mongodb-2.2").getElements();
			
		JsonNode cred =  (JsonNode)dbNode.next().get("credentials");
			
		String uri =cred.get("url").getTextValue();
			
		logger.debug ("url db: " + uri);
			
		return uri;
			
	} catch (JsonGenerationException e) {
		logger.debug(e.getMessage());
	} catch (JsonMappingException e) {
		logger.debug (e.getMessage());
	} catch (IOException e) {
		logger.debug (e.getMessage());
	}
			
	return null;
}

Вы можете создать конфигурационный класс Spring для формирования соединения с базой данных с использованием драйвера Java MongoDB (с целью возвращения объекта базы данных клиенту).

@Configuration
public class MongoConfiguration {

	public @Bean DB mongoDb() throws Exception {
		MongoClientURI mcUri = new MongoClientURI(getUrlConnection());
		MongoClient mc = new MongoClient(mcUri);
		mc.getDB(mcUri.getDatabase());
		return mc.getDB(mcUri.getDatabase());
	}

Класс UserManager использует MongoConfiguration для взаимодействия с базой данных. Метод init получает коллекцию users (пользователи) или создает такую коллекцию, если она не существует.

private void init(){		
		ApplicationContext ctx = 
	    new AnnotationConfigApplicationContext(MongoConfiguration.class);
		db = (DB) ctx.getBean("mongoDb");
		coll = db.getCollection("users");
	}

Напоминаю, что MongoDB — это не реляционная, а ориентированная на документы СУБД. Это означает, что вместо таблиц у нас есть коллекции; вместо строк (или записей) — документы; а вместо столбцов — поля. Эти поля не определены заранее, как это происходит в случае столбцов в таблице. В коллекцию можно ввести любую разновидность данных. Чтобы найти документ, необходимо создать объект BasicDBObject.

	BasicDBObject user = new BasicDBObject("username", userData.getUsername());
	
	return (DBObject)coll.findOne(user);

UserController — это клиент класса UserManager; он использует свои функции для получения и сохранения информации о вошедшем в систему пользователе.

	@RequestMapping(value="/user", method = RequestMethod.GET)
	public  @ResponseBody String getUser(@RequestParam("username") String username, 
@RequestParam("password") String password)  {
		logger.debug("BEGIN: controller getUser - username:" + username);
		UserManager userManager = new UserManager();
		BasicDBObject user = (BasicDBObject) userManager.getUser(username, password);		
		logger.debug("END: controller getUser - user:" + user);

		return user.toString();
	}

Шаг 6. Развертывание Spring-приложения

Приложение MyVacations обращается к приложению UserService за информацией о пользователе для профилирования поиска посредством значений, которые приложение UserService сохранило при последнем входе этого пользователя в систему. Пользователь может визуализировать на карте набор отелей по результатам поиска. Кроме того, в этом случае контроллеры не конфигурируются в конфигурационном xml-файле, а динамически обнаруживаются средой Spring Framework — благодаря наличию следующей директивы в конфигурационном файле сервлета.

     <context:component-scan base-package="com.myvacations.app" />

Вызов первого контроллера (HomeController) служит для отображения страницы входа в систему.

	/**
	 * Просто выбирает представление home для отображения страницы входа (login).
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		
		return "login";
	}

Контроллер HotelsController активируется после вывода страницы входа в систему.

Этот контроллер, вызываемый посредством HTTP-запроса get с параметром username (имя пользователя), обращается к UserService с целью получения предпочтений пользователя, сохраненных при последнем обращении этого пользователя.

Этот контроллер использует RestTemplate для осуществления RESTful-вызова приложения UserService. RestTemplate — это helper-класс Spring для клиентской стороны HTTP-доступа. Объекты передаются в методы getForObject() и возвращаются обратно, а их преобразование в HTTP-запросы и в HTTP-ответы осуществляет класс HttpMessageConverters. В нашем примере этот класс используется для вызова RESTful-сервисов, таких как UserService.

@RequestMapping(value = "/hotels", method = RequestMethod.GET)
public String getHotels(@ModelAttribute("username") String username,Model model) {
		
	logger.debug("BEGIN HotelsController: username=" + username);

	RestTemplate restTemplate = applicationContext.getBean("restTemplate", 
	RestTemplate.class);

	user = (UserData) restTemplate.getForObject(new 
	URI("http://userservice.ng.BlueMix.net/userpref?username="+username), 
	UserData.class);

Шаг 7. Интеграция сервисов

Чтобы добавить в приложение MyVacations больше данных, можно интегрировать соответствующие сервисы. Я покажу два примера интеграции сервисов.

  • RESTful-сервис Expedia для получения информации об отелях.
  • Сервис Google Maps для визуализации этих отелей на карте.

В первую очередь осуществим интеграцию веб-сервиса Expedia для получения информации об отелях. Сначала необходимо посетить сайт Expedia API Developer и зарегистрироваться для получения учетной записи разработчика и API-ключа.

Контроллер SearchController, активируемый нажатием кнопки Submit, представляет собой Ajax-вызов в Hotels.jsp.

    $.get('/search',
      $("#ricerca").serialize(),
      function (data, status) {
        if (status == 'success') {                	
            if (data.toString()==""){
                $("#map_canvas").hide();

В SearchController имеется вызов в ExpediaClient для получения списка HotelSummary List.

	List<HotelSummary> response=null;
	response = (new ExpediaClient()).getHotels(
			location, dateFrom, dateTo, numAdults, numChildren);

Используя информацию о пользователе, полученную приложением UserService, ExpediaClient извлекает объекты ExpediaObjects посредством декодирования JSON-ответа от Expedia.

Листинг 1. Извлечение списка отелей
ExpediaObjects hotels= (ExpediaObjects) 
restTemplate.getForObject(buildQueryHotelsURL(location,dal,al,numAdulti,numBambini),
ExpediaObjects.class);

Затем в нашем примере используется сервис Google Maps для визуализации на карте списка отелей, полученного из сервиса Expedia.

Google Maps API позволяет встроить изображение карты Google в веб-страницу. Для начала необходимо получить соответствующий API-ключ от Google. Этот ключ предоставляется бесплатно, однако необходимо создать учетную запись Google.

<script src="//maps.googleapis.com/maps/api/js?
key=YOUR_API_KEY&sensor=TRUE_OR_FALSE"></script>

Для взаимодействия с Google Maps API я выбрала плагин jQuery-ui-map. Это хороший jQuery-плагин для встраивания карт в веб-приложения и в мобильные приложения. Он позволяет просматривать карты и маркеры, использовать усовершенствованные сервисы, а также управлять следами, просмотром в режиме street-view и динамической загрузкой географических данных, представленных в формате JSON.

После создания div или другого подобного HTML-контейнера следует запустить gmap (ключевой метод этого плагина, который позволяет вызывать функции Google Maps API) с целью отправки на отображение координат ссылки маркера на карте.

var map =  $('#map_canvas').gmap({
     'center': new google.maps.LatLng(data[0].latitude,data[0].longitude),
     'minZoom': 5,
     'zoom': 8
         });

Мы создали карту, центром которой являются географические координаты первого отеля.

Теперь для каждого отеля в результирующем списке мы создаем маркер и помещаем его на карту.

      $.each(data, function (i, m) {
         $('#map_canvas').gmap('addMarker', {
            'position': new google.maps.LatLng(m.latitude,
                            m.longitude),
            'bounds': true
         }) .click(function () {…

Зарегистрируйте функцию для on-click-события с целью загрузки информационного окна с кратким описанием отеля.

  $('#map_canvas').gmap('openInfoWindow', {
                                        'content': descr
                                    },

Шаг 8. Развертывание приложения в облаке

После построения и создания приложений мы готовы развернуть их на платформе Bluemix. Развертывание представляет собой автоматическое перемещение приложений с локальной виртуальной машины на виртуальную машину в облачной среде.

Для инициирования развертывания используйте команду push CLI-интерфейса cf, предоставляемого проектом Cloud Foundry. Имейте в виду, что в документации Cloud Foundry процесс развертывания часто называется проталкиванием (push) приложения.

cf push MyVacations –path MYDIR\MyVacations.war

Команда push выполняет разнообразные подготовительные задачи, такие как отыскание контейнера для исполнения приложения, инициализация контейнера с выделением соответствующих программных средств и системных ресурсов, запуск одного или более экземпляров приложения, сохранение ожидаемого состояния приложения в базе данных Cloud Controller.

Шаг 9. Тестирование приложения

С целью проверки приложения сначала выполним простой тестовый прогон этого приложения, а затем посмотрим на его переносимость.

Для упрощения страница входа в систему разрешает доступ с любыми именами и паролями пользователя; если пользователь еще не присутствует в системе, то приложение создает соответствующую запись.

После входа в систему перейдите на страницу поиска, задайте поисковые параметры и нажмите Search.

Search page with result map
Search page with result map

При нажатии на какой-либо из маркеров отображается краткая информация о соответствующем отеле.

Window with brief information about the hotel
Window with brief information about the hotel

Чтобы проверить возможность переноса моего приложения на различные облачные платформы, я решила развернуть приложение MyVacations на платформе Pivotal и на платформе Google App Engine.

При развертывании на платформе GoogleAE применяются следующие инструменты и технологии.

  1. Google App Engine Java SDK 1.8.8
  2. Spring 3.1.1
  3. Eclipse 4.2+ Google plugin for Eclipse

Поскольку платформа Google App Engine поддерживает веб-приложения Java на основе Spring Framework, мое приложение не нуждается ни в каких изменениях.

Пакет Google App Engine SDK (установленный в Eclipse) содержит веб-сервер для тестирования вашего приложение в имитационной среде, поэтому вы сможете протестировать это приложение без учетной записи пользователя Google (кроме того, можно запустить приложение на исполнение на удаленном сервере Google).

Плагин Google для Eclipse добавляет в меню Run элементы для запуска этого сервера. В этом сценарии приложение MyVacations, установленное на платформе Google App Engine, вызывает приложение UserService, установленное на платформе Bluemix, посредством его RESTful API-интерфейса. Это демонстрирует высокий уровень переносимости приложения, которое не использует внутренних сервисов платформы.

>Google App Engine at localhost
>Google App Engine at localhost

При осуществлении развертывания на платформе Pivotal применяются следующие инструменты и технологии.

  1. Spring 3.1.1
  2. JDK 7
  3. Spring Tool Suite 3.4.0+ Cloud Foundry Integration for Eclipse 1.5.1

Плагин Cloud Foundry Integration for Eclipse позволяет произвести развертывание на платформе Pivotal. Так можно протестировать приложение непосредственно в целевой среде без выхода из IDE. Вам потребуется действительная учетная запись пользователя Pivotal.

Deployment on Pivotal Cloud Foundry
Deployment on Pivotal Cloud Foundry

На этой платформе вы сможете развернуть это приложение без изменений, а приложение UserService – с минимальными изменениями.

Заключение

Рассмотренное приложение демонстрирует лишь некоторые возможности интеграции внутренних и внешних сервисов с облачным приложением. Я воспользовалась некоторыми преимуществами ClueMix:

  • Сокращение требований к инициализации (приложения или инфраструктуры)
  • Масштабируемость
  • Простота интеграции внутренних сервисов
  • Упрощенное управление
  • Переносимость на аналогичные облачные платформы

Что касается переносимости, то платформа Bluemix базируется на Cloud Foundry, а это обеспечивает полную свободу переноса на другие платформы. Я продемонстрирую переносимость на двух примерах.

  • Развертывание MyVacations в облаке Pivotal не требует никаких изменений. Платформа Pivotal также базируется на Cloud Foundry, поэтому совместимость является почти полной. Приложение UserService на платформе Bluemix использует сервис MongoDB; у платформы Pivotal имеется аналогичный доступный сервис MongoDB, однако в классе MongoConfiguration необходимо изменить URL-соединение на указанное в переменной VCAP_SERVICES.
  • В случае облачной среды, не основанной на проекте Cloud Foundry (в нашем случае это облако Google), развертывание приложения MyVacations по-прежнему не представляет каких-либо трудностей (MongoDB не входит в число сервисов, предоставляемых платформой Google, поэтому я выбрала иной подход — сервис Big Table, который в отличие от MongoDB является закрытым решением для обеспечения персистентности данных). Для использования Google Application Engine достаточно добавить файл appengine-web.xmlfile в каталог web-inf.
    <?xml version="1.0" encoding="utf-8"?>
    <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
      <application>_your_app_id_</application>
      <version>1</version>
      <threadsafe>true</threadsafe>
    </appengine-web-app>

В заключение надо отметить, что при проектировании облачного приложения необходимо использовать возможности распределенной платформы и обеспечить предоставление надежного, эффективного и быстрого сервиса. Я думаю, что в целом в приложении MyVacations я достигла этой цели. Конечно, когда приложение UserService получает большое количество запросов, возможно увеличение времени отклика для пользователей. Однако в этом случае вы сможете поэкспериментировать с различными приемами для повышения производительности (такими как использование асинхронного обмена сообщениями для разъединения компонентов с целью предотвращения блокировки задачи до получения ответа). Существует несколько приемов для настройки производительности, которые можно применить к приложению этому типа; предлагаю читателям поэкспериментировать с этими приемами.

Благодарности

Выражаю глубокую благодарить Фабио Кастильони (Fabio Castiglioni) за поддержку и за рецензирование этой статьи.

Оригинал статьи: Build a portable Java travel app that integrates web services


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Облачные вычисления, Web-архитектура
ArticleID=992030
ArticleTitle=Пример построения переносимого Java-приложения, интегрирующего веб-сервисы
publish-date=12052014