Работа с Worklight: Часть 2. Разработка структурированных модулей и использование функциональности Encrypted Offline Cache в IBM Worklight

Разделение приложения на уровни по структуре, функциональности и безопасности

В данной серии статей описывается платформа IBM® Worklight® и рассматривается создание мобильных приложений, использующих разнообразные продукты IBM. Во второй части продолжается описание разработки Worklight-приложения, демонстрирующей передовой опыт создания гибридных приложений и функциональность Worklight Encrypted Offline Cache.

Карлос Андреу, разработчик ПО, IBM

Карлос Андреу (Carlos Andreu) является разработчиком программного обеспечения в IBM Software Group. В настоящее время работает над интегрированной средой для создания гибридных, Android- и iOS-приложений. Диапазон его интересов - от новейших тенденций и технических блогов до чтения, телевидения и музыки разных стилей. Узнать о нем подробнее можно на сайте http://dev.yapr.org/carlosandreu.



Джереми Норти, разработчик ПО, IBM

Джереми Норти (Jeremy Nortey) является разработчиком программного обеспечения для IBM Mobile Foundation в IBM Software Group. Он занимается разработкой ПО и обеспечением качества мобильных решений. Специализируется на iOS, а в свободное время занимается созданием встроенных приложений для iPhone. Его хобби: футбол и бег.



Радж Баласубраманьян, ИТ-архитектор, консультант, IBM

Радж Баласубраманьян (Raj Balasubramanian) является архитектором продукта для IBM Mobile Foundation в IBM Software Group. Руководит работой над взаимодействием клиент/сервисы для IBM Worklight и IBM Mobile Foundation. До этого занимался проектами доставки конечным пользователям приложений и инфраструктуры, использующих технологии SOA, BPM, Web 2.0 и Portal. Диапазон его интересов - от различных отраслей техники до истории, математики и физики. В настоящее время готовится получить степень доктора философии в Техасском университете, Остин. Узнать о нем подробнее можно в его блоге Gurukulam на сайте http://balasubramanians.com/blog.



10.04.2013

Введение

IBM Worklight, входящая в IBM Mobile Foundation, является надежной платформой для быстрого создания мобильных Web-приложений, работающих на различных устройствах. В этой статье рассматриваются пропущенные в первой части вопросы разработки полнофункционального автономного мобильного приложения под названием Todo, которое позволяет пользователю создавать и поддерживать список задач. Вы узнаете об Encrypted Offline Cache, средстве безопасности в клиентской среде исполнения Worklight, которое защищает конфиденциальную информацию от вредоносных атак и кражи устройства.

Получите Worklight

IBM Worklight Developer Edition 5.0

В вашей интегрированной среде разработки Eclipse должна быть установлена IBM Worklight Studio, и вы должны уметь разворачивать простое приложение Hello World на IOS и Android. Займемся приложением Todo, разработку которого начали в первой части. Загрузите исходное приложение, которое создали в первой части, и импортируйте его в свою среду Worklight Studio. (Кроме того, вы можете загрузить файлы проекта приложения Todo, включенные в эту статью.)


Разработка приложения

Использование примера приложения

На рисунке 1 показана обобщенная блок-схема выполнения мобильного приложения Todo. Пользователь выполняет следующие операции:

  1. Открыть приложение.
  2. Ввести код доступа для безопасного хранения автономных данных и нажать Start, чтобы перейти на вторую панель.
  3. Ввести в первое поле текст, который представляет собой новую задачу, а затем добавить ее в список, нажав кнопку Add Item.
  4. Пометить элемент как выполненный, выбрав его в списке. Нажать Remove Done, чтобы удалить все элементы, помеченные как выполненные.
  5. Отфильтровать элементы, отображаемые на панели, введя во второе текстовое поле полное имя или часть имени элемента и нажав Filter Items....
Рисунок 1. Панели примера приложения
Рисунок 1. Панели примера приложения

Первый шаг по добавлению функциональности – разделение кода на модули, выполняющие определенные функции в рамках приложения. Приложение Todo будет состоять из следующих модулей (см. рисунок 2):

  • Модуль Constant получает и устанавливает различные константы, которые будут использоваться в приложении.
  • Модуль List отвечает за хранение списка событий текущей сессии и событий обработки, таких как добавление в список нового элемента, выбор выполненных элементов и удаление элементов, помеченных как выполненные.
  • Модуль Vault выполняет шифрование и дешифрование списка. Здесь будет использоваться функция Encrypted Offline Cache.
Рисунок 2. Модули и их взаимодействие
Рисунок 2. Модули и их взаимодействие

Для структурирования кода будет использоваться широко распространенный и популярный в JavaScript-мире шаблон Module Pattern (см. раздел Ресурсы). В листинге 1 показан скелет модуля. Объекту Module1 из пространства имен MyApp присваивается самовыполняющаяся (или одноразовая) функция. Рекомендуется передавать глобальные объекты (в большинстве JavaScript-сценариев на стороне клиента это окно) и библиотеки сторонних поставщиков (например, JQuery), чтобы внутри модуля можно было использовать $, даже если что-нибудь еще использует $ за пределами модуля (например, Prototype.js или другая библиотека). Также рекомендуется перечислять зависимости и назначать зависимостям локальные переменные, потому что доступ к локальным переменным требует меньше времени, чем поиск пар ключ-значение.

Например,

var eoc = WL.EncryptedCache; eoc.open()

лучше, чем выполнять JavaScript-поиск объекта WL, а затем искать ключ EncryptedCache, который указывает на другой объект, содержащий открытую функцию, вызываемую всякий раз, когда к ней обращается один из ваших модулей.

Листинг 1. Скелет Module Pattern
MYAPP.Module1 = (function (global, $) { 
//Список зависимостей: jQuery 1.7.2

//Список частных переменных

//Список частных функций
var _init = function () {
console.log("I'm ready!");
}

//Список процедур единовременной инициализации 

//общедоступный программный интерфейс
return {
init : _init
};

}(window, jQuery)); //MYAPP.Module1

Самое время обсудить некоторые соглашения о стиле кодирования. В наших примерах прописные буквы используются для констант, а символы подчеркивания добавляются перед частными функциями, чтобы отличать их от частных переменных. К этим соглашениям можно добавить использование шаблона единственной инструкции var, когда единственная переменная (var) объявляется в самом начале функции. Причина в том, что JavaScript при выполнении кода (листинг 2) "поднимает" переменные к его началу, поэтому, забыв использовать var, иногда можно получить нежелательные результаты, например, перезапись глобальной переменной с тем же именем.

Листинг 2. Пример подъема
//Пример подъема
myname = "global";

function func() {
alert(myname); //возвращает: "undefined" 
var myname = "local";
alert(myname); //возвращает: "local"

Существует базовая конструкция под названием "замыкание", которая приводит в действие модули (см. раздел Ресурсы). В JavaScript замыкание обращается к локальной переменной функции, которая остается жить после возвращения управления. Это тесно связано с областью видимости в JavaScript, которая определяется функцией. В листинге 1 показан пример замыкания в действии. Здесь _init является локальной функцией, которая недоступна за пределами самовыполняющейся функции, назначенной MYAPP.Module1, но вы возвращаете объект, который имеет ссылку на эту локальную функцию; таким образом, к ней можно легко обратиться следующим образом: MYAPP.Module1.Init (). Этого можно добиться при помощи замыкания.

JavaScript является событийно-ориентированным языком программирования, сродни шаблону Observer Pattern (Наблюдатель), описанному практически в каждой книге по шаблонам программирования. В сценарии JavaScript события инициируются всякий раз, когда, например, пользователь выполняет прокрутку, нажимает на HTML-тег или наводит курсор на ссылку. Можно прослушивать события, а можно инициировать свои собственные события.

Важно также следовать принципу разделения задач, что означает раздельное хранение разметки, стиля и логики приложения. HTML-код должен описывать только структуру приложения; встраивать JavaScript-функции в HTML нежелательно. Этого можно добиться, используя управляемый событиями стиль кодирования, когда вместо определенного элемента со встроенным вызовом отслеживается определенное событие, которое будет инициировать необходимое действие. Это позволяет иметь логику приложения в одном месте, в JavaScript, а не в нескольких местах по всему HTML. Кроме того, вместо включения HTML в JavaScript следует использовать шаблоны (см. раздел Ресурсы).


Модуль Constant

Начните с определения пространства имен (см. листинг 3). Для примера приложения Todo назовите пространство имен TD. Пространство имен – это, по существу, объект, который содержит другие объекты, относящиеся к приложению. Вполне возможно, что при росте приложения пространство имен TD уже будет определено в глобальном пространстве имен. Используйте шаблон единственной инструкции var для создания TD объекта, если он еще не создан. Функция пространства имен будет обеспечивать согласованность при вызове метода: при вызове TD.namespace(TD.module1) в TD будет добавлен объект под именем module1. Вы увидите, как можно объявить пространство имен TD и использовать его для каскадных объектов по мере необходимости.

Листинг 3. Функция пространства имен
/**********************************************************************************
* TD NAMESPACE
**********************************************************************************/
var TD = TD || {};

TD.namespace = function (ns_string) { 
var parts = ns_string.split('.'),
parent = TD, 
i;

// удаление избыточности
if (parts[0] === "TD") {
parts = parts.slice(1); 
}

for (i = 0; i < parts.length; i += 1) {
// создание свойства, если оно не существует
if (typeof parent[parts[i]] === "undefined") {
parent[parts[i]] = {}; 
}
parent = parent[parts[i]]; 
}

return parent; 
};

На рисунке 3 показано, что TD.Constant (модуль Constant) не имеет локальных элементов. Он только возвращает значения посредством того, что мы называем общедоступным программным интерфейсом (это просто пара ключ-значение, созданная с использованием литерала объекта). Аналогичным образом для Encrypted Offline Cache используется ключ элементов Todo. Значения шифрования и дешифрования – это простые булевы величины, указывающие модулю Vault выполнить шифрование или дешифрование.

Рисунок 3. Элементы модулей
Рисунок 3. Элементы модулей

Модуль List

Как показано на рисунке 1 и в листинге 4, вторая панель является, по существу, главной страницей приложения. Она содержит:

  • Логотип в заголовке.
  • Текстовое поле (id="new-todo"), которое будет использоваться для добавления новых элементов.
  • Кнопка для добавления новых элементов (id="add").
  • Кнопка для удаления всех элементов, помеченных как выполненные (id="delete").
  • Неупорядоченный список элементов (id="todo-list").
  • Тег div (id="no_items") для отображения текста, если список пуст.

Также обратите внимание, что ничего не стоит получить отфильтрованные данные, добавив свойство data-filter='true' в тег <ul> (см. раздел Ресурсы). (http://jquerymobile.com/demos/1.1.0/docs/lists/index.html).

Листинг 4. HTML главной панели
<!-------------------------- MAIN PAGE  -------------------------->
<div data-role="page" id="main" >

<!-- <div data-role="header"><h1>Todo App</h1></div> -->
<img class="centerimg" src="images/logonobg.png"/>

<div data-role="content" >

<input type="text" name="new-todo" id="new-todo" 
placeholder="Введите ваши задачи..." />

<div class="controls" data-role="controlgroup" data-type="horizontal">
<a href="#" id="add" data-role="button" data-icon="plus">Add New</a> 
<a href="#" id="delete" data-role="button" data-icon="delete">Delete Done</a>
</div>

<ul id="todo-list" data-role="listview" data-filter="true" data-inset="true" >
</ul><!-- /todo-list -->

<div class="center" id="no_items"></div>

</div>
<!-- /content -->
</div>
<!-- /page -->

Элементы вашего списка задач хранятся в массиве list. Например,

[{content: "myTodo Item 1", done: false},
{content: "myTodo Item 2", done: true}].

Это простой массив пар ключ-значение (объектов JavaScript), состоящих из строки имени элемента и логического значения, указывающего, выполнен элемент или нет.

Как показано на рисунке 3, TD.List (модуль List) содержит локальный метод (_encrypt), который шифрует список (локальный массив под именем list), вызывая метод шифрования модуля Vault из его общедоступного интерфейса. В нем также есть сгенерированная из шаблона функция _item_template для отображения элементов, определенных в HTML-документе; вам нужно передать объект, имеющий item для ключа и массива list.

Упражнение

Частная функция _add проверяет, является ли item.content пустой или неопределенной и возвращает ли что-нибудь вызов item.done. Напоминаем, что переменные, которым не было присвоено значение, по умолчанию возвращают undefined (не определена).

Листинг 5. Функция _add
_add =  function (item) {
if (item.content !== 'undefined' && item.done !== 'undefined' && item.content.length > 0)
{
list.push(item);
}
},// добавить элемент

Функция _refresh_list динамически выполняет map-функцию по массиву list и добавляет новый элемент к каждому объекту, хранящемуся в массиве. Оставшаяся часть функции очищает список, добавляет шаблон, сгенерированный на основе списка, в HTML-тег ul, и обновляет представление. Последним вызывается метод библиотеки jQuery Mobile для обновления стиля списка.

Листинг 6. Функция _refresh_list
_refresh_list = function () {

$.map(list, function (item, i) {
item.index = i;
});

list_ul.empty();
list_ul.append(_item_template({item : list}));
list_ul.listview('refresh');

if (list.length < 1) {
no_items.text('No items.');
} else {
no_items.text('');
}

}; // обновить список

Еще одно упражнение

Далее мы прикрепим к событиям действия. Если вы знакомы с jQuery, то наверняка видели или использовали вызовы, такие как bind(), live() и delegate(). В нашем примере используется исключительно on(), потому что, начиная с JQuery 1.7.0, методы bind, live и delegate являются просто обертками для on(). Кроме того, $(this) "кешируется" в локальную переменную $this, потому что в идеале желательно как можно реже использовать функцию JQuery и ограничить количество обращений к DOM (Document Object Module – объектная модель документа), потому что это очень затратная операция. Обратите внимание, что булево значение, показывающее, выполнен элемент или нет, меняется на противоположное, а также добавляется или удаляется класс done, определенный в таблице стилей (файл .css). В конце выполнятся encrypt() для списка, даже если сразу после нажатия на элемент (чтобы пометить его как выполненный) вы закрываете приложение.

Листинг 7. Прикрепления событий в TD.List
add_btn.on('click', function () {

_add({content : new_item.val(), 
done: false });

new_item.val('');
_refresh_list();

_encrypt();

}); // добавить btn

list_ul.on('click', 'li', function () {

var $this = $(this),
$item = $this.find('a'),
index = $item.attr('id'), 
current_item = list[index];

current_item.done = !(current_item.done);

if (current_item.done) {
$item.addClass('done');
} else {
$item.removeClass('done');
}

_encrypt();

}); // элемент списка

delete_btn.on('click', function () { 
var i = list.length;

while (i--) {
if (list[i].done) {
list.remove(i);
}
}

_refresh_list();

_encrypt();

});// удалить btn

Аналогичная процедура применяется для кнопки удаления, код которой можно просмотреть в Worklight Studio.

Общедоступный интерфейс для модуля List очень прост: можно получить текущий список, задать новый список и обновить только что установленный список. Мы вызываем эти методы, указывая пространство имен TD, затем имя модуля и, наконец, один из ключей объекта, возвращаемого модулем.

Например,

TD.List.get(), TD.List.set([]), TD.List.refresh()

правильно, а

TD.List._refresh_list(), TD.List._add()

неправильно, потому что эти функции могут быть вызваны только внутри модуля (помните, функции создают область видимости) или привилегированными элементами (например, объектом, который возвращен внутри модуля).

Листинг 8. Общедоступный интерфейс TD.List
//общедоступный программный интерфейс
return {
get : function () {
return list;
},
set : function (new_list) {
list = new_list;
},
refresh : function () {
_refresh_list();
}
};

Модуль Vault

Модуль TD.Vault является оберткой для Worklight-функциональности Encrypted Offline Cache (EOC). Построенная на локальном хранилище HTML5, EOC является способом хранения персистентных данных без использования cookie-файлов. EOC предоставляет методы открытия и закрытия, а также чтения и записи значений. Подробные сведения о безопасности применения EOC выходят за рамки данной статьи, но в двух словах скажем, что EOC использует пользовательский пароль для шифрования данных. Таким образом, HTML-код, имеющий отношение к данному модулю, представляет собой страницу (первая панель на рисунке 1), содержащую:

  • Заголовок с логотипом или именем приложения.
  • Поле для кода доступа (id=’passcode’).
  • Кнопку (ID = 'send_passcode') отправки кода доступа.
Листинг 9. HTML-код страницы ввода кода доступа
<!-------------------------- PASSCODE PAGE  -------------------------->
<div data-role="page">

<img class="centerimg" src="images/logonobg.png"/>

<div data-role="content">

<div class="center" id="invalid_passcode"></div>

<ul data-role="listview" data-inset="true">
<li data-role="fieldcontain">
<label for="passcode">Enter Passcode:</label> 
<input type="text" name="passcode" id="passcode" value=""
placeholder="Ваш код доступа" />
</li>
</ul>

<a id="send_passcode" href="#" data-role="button" data-theme="a">Start</a>
</div>
<!-- /content -->

</div>
<!-- /page -->

Начните JavaScript-код с передачи в самовыполняющуюся функцию глобальных переменных, например JQuery (переопределена как $) и WL (пространство имен Worklight, переопределенное как WL). Затем "кэшируйте" зависимости, назначив им локальные переменные. Это гарантирует, что вы сможете легко вызвать wl.EncryptedCache.open (а не передавать в модуль пространство имен WL в качестве параметра), а затем кэшировать wl.EncryptedCache в локальной переменной eoc. Затем частные переменные (например, KEY, содержащая вводимую пользователем идентификационную фразу) и некоторые вызовы DOM сохраняются для кнопки кода доступа, поля ввода кода доступа и тега div, отображающего сообщение об ошибке, если код доступа неправильный (см. листинг 10).

Листинг 10. Модуль TD.Vault, зависимости и частные переменные
TD.namespace('TD.Vault');
TD.Vault = (function ($, wl) { 
//зависимости
var eoc = wl.EncryptedCache,
log = wl.Logger,
list_obj = TD.List,
CONST = TD.Constant,

//частные переменные
KEY = "",
send_passcode_btn = $('#send_passcode'),
passcode_fld = $('#passcode'),
invalid_passcode_div = $('#invalid_passcode'),

Последнее упражнение

Теперь у вас есть ряд частных переменных, например, _error, которая просто регистрирует сообщение об ошибочном обратном вызове. Методы _setData и _getData указывают, как получить и установить данные для шифрования или дешифрования (см. листинг 11).

Листинг 11. Функции _error, _setData и _getData
//частные функции
_error = function () {
log.debug("error");
},

_setData = function (new_list) {
if (new_list) {
list_obj.set(new_list);
list_obj.refresh();
} 
},

_getData = function () {
return list_obj.get();
},

Определение события аналогично модулю TD.List, где send_passcode_btn прикрепляется к нажатию кнопки и просто присваивает код доступа, введенный пользователем при запуске приложения, частной переменной KEY, если длина ввода составляет не менее 1 символа (см. листинг 12).

Листинг 12. Прикрепление события к кнопке Send Passcode
send_passcode_btn.on('click', function () {

var passcode = passcode_fld.val();

if (passcode.length > 0) {
KEY = passcode;

_decrypt();

$.mobile.changePage("#main", { transition: CONST.DEFAULT_TRANSITION });

} else {
passcode_fld.val('');
invalid_passcode_div.text('Invalid Passcode.');
}

});

Затем содержимое EOC дешифруется путем вызова частной переменной _decrypt(). Она вызывает _open с константой (CONST.DECRYPT), означающей, что вы собираетесь выполнить дешифрование после открытия зашифрованного кэша. Функция _open инициирует событие eoc-open и посылает действие (decrypt), в _read(). После этого нужно попытаться прочитать зашифрованный кэш с использованием ключа, предоставленного пользователем. В данный момент _setData вызывается с использованием данных, возвращенных при анализе в массив JavaScript при помощи parseJSON (этот массив содержит пару ключей значение, описывающую один элемент на каждый индекс массива). Получив что-нибудь из кэша, вы вызываете модуль List для установки нового списка. Наконец, из общедоступного интерфейса модуля TD.List выполняется обновление HTML-элемента списка.

Листинг 13. Функции _close, _write, _read, _encrypt, _decrypt и прослушиватель события eoc-open
_close = function () {
var onCompleteHandler = function () { 
$.publish('eoc-closed'); 
};

//function(onCompleteHandler, onErrorHandler)
eoc.close(onCompleteHandler, _error);
},

_write = function () {
var data = JSON.stringify(_getData());

//function(key, data, onCompleteHandler, onErrorHandler)
eoc.write(CONST.TODO_ITEMS_KEY, data, _close, _error); 
},

_read = function () {
var onCompleteHandler = function (data) {
_setData($.parseJSON(data)); 
_close(); 
};

//function(key, onCompleteHandler, onErrorHandler)
eoc.read(CONST.TODO_ITEMS_KEY, onCompleteHandler, _error);
},

_encrypt = function () {
_open(CONST.ENCRYPT);
},

_decrypt = function () {
_open(CONST.DECRYPT);
};

$.subscribe("eoc-open", function (e, action) {
if (action) { // == CONST.ENCRYPT
_write();
} else { // == CONST.DECRYPT
_read();
}
});

При шифровании выполняются в основном те же действия, за исключением _write вместо _read после открытия кэша. Затем запускается _getData, чтобы получить текущий список, преобразовать его в строку с помощью JSON.stringify и сохранить ее в кэше. Закрывайте кэш после каждого шифрования и дешифрования.

На рисунках 4 и 5 показано готовое приложение, выполняющееся на iPhone Simulator.

Рисунок 4. Готовое приложение, выполняющееся на iPhone Simulator (страница ввода кода доступа)
Рисунок 4. Готовое приложение, выполняющееся на iPhone Simulator (страница ввода кода доступа)
Рисунок 5. Готовое приложение, выполняющееся на iPhone Simulator (главная страница)
Рисунок 5. Готовое приложение, выполняющееся на iPhone Simulator (главная страница)

Заключение

Во второй части вводной серии статей по IBM Worklight было продолжено использование среды разработки Worklight, установленной в первой части, и добавлена функциональность для создания примера приложения Todo. Вы узнали, как структурирование кода помогает разработке, представлению и обслуживанию приложения. Вы также узнали о сохранении данных (список задач) на устройстве с помощью предоставляемой Worklight функциональности Encrypted Offline Cache. В последней части серии мы завершим пример приложения, добавив серверное подключение при помощи адаптеров.


Загрузка

ОписаниеИмяРазмер
Файлы примера проекта приложенияtodo-app-part2.zip18.5МБ

Ресурсы

Научиться

Получить продукты и технологии

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Мобильные приложения, WebSphere
ArticleID=863911
ArticleTitle=Работа с Worklight: Часть 2. Разработка структурированных модулей и использование функциональности Encrypted Offline Cache в IBM Worklight
publish-date=04102013