9 янв. 2011 г.

Play Framework + Scala + GAE

Облака... Облачные вычисления... Данные в облаке. Все чаще мы слышим эти высказывания. Кому-то это кажется в новинку, кто-то активно использует, а кто-то панически боится. Если вам близко последнее, то я вас поздравляю, вы параноик. Шучу) Конечно в наше время информация это все. Кто владеет самой свежей и ценной информацией, тот владеет миром. Как же выглядит это "облако" ? Ну для начала давайте будем называть вещи своими именами, а именно Cloud Computing. Говоря простым языком вы "покупаете" вычислительные ресурсы. Причем в отличии от обычного хостинга вы платите только за использованные ресурсы. Более точнее за меня сможет об этом рассказать wikipedia. Заманчиво правда ? А игроков на рынке облачных вычислений не так уж и мало, самые видные: Amazon Web Services, Google App Engine, Windows Azure, Stax.Net Многие из них предоставляют ограниченное кол-во своих мощностей совершенно бесплатно! Я выбрал Google App Engine.

Отлично, с хостингом определились, нужно создавать приложение. И так, что мы там имеем на App Engine. Ага, Python и Java. С первым я знаком плохо, а вот последний знаю довольно хорошо. Приступим, но стоп! Как мы будем писать наше приложение, все с нуля ? Хм... Пожалуй нет. Давайте посмотрим. Для того же Ruby есть Ruby On Rails, для Python - Django, TurboGears, а для Java целый вагон разных фреймворков. Но все ли они подойдут ? Нет. Дело в том что Google App Engine не использует привычные всем РСУБД, а в основе хранения данных царит BigTable. Большинство Java-фреймворков сразу отпадают. Но не стоит отчаиваться! Комьюнити пользователей App Engine ведет и периодически обновляет замечательный перечень поддерживаемых технологий в GAE.

Приступим к выбору удобного нам MVC-фреймворка. Я выбрал Play Framework. Хотя на момент написания статьи его не было в перечне поддерживаемых технологий, но работает с GAE он уже сейчас довольно неплохо. Почему выбрал именно его ? Он предельно прост, лаконичен, не тянет за собой привычные для Java-фреймворков портянки XML-файлов. Кому-то он кажется похожим на Ruby On Rails, кому-то на Django. Как бы там не было Play! предоставляет весь необходимый функционал, а так же неплохую библиотеку плагинов. Устанавливается предельно просто и работает сразу "без бубна".

Отлично, выбрали фреймворк. Как всегда нам хочется писать как можно меньше кода. Возможно с Ruby / Python это бы получилось, т.к. эти языки более "заточены" под веб. У нас Java. Не хочу обидеть фанатов этого языка, но в плане нового функционала "из коробки" Java давно уже не развивается. Возможно Oracle исправит это положение, но вот выход Java 7 опять отложили. И тут у нас есть выход. На горизонте уже давно видны две красавицы: Scala и Clojure. Эти языки базируются на JVM, а значит могут поддерживать весь багаж библиотек java-сообщества. Языки появились неспроста. Как вы наверное заметили сейчас идет тенденция на функциональное программирование. Чем это обусловлено ? Закон Мура изменился. Ранее Мур высказал предположение, что число транзисторов на кристале будет удваиваться каждые 24 месяца. До недавного времени это работало, частота процессоров увеличивалась с заявленной очередностью.
Однако в 2007 году Мур заявил, что закон, очевидно, скоро перестанет действовать из-за атомарной природы вещества и ограничения скорости света
(см. wikipedia)
Так и произошло. Увеличивать тактовую частоту процессоров больше не было возможности и производители компьютерного "железа" начали выпускать многоядерные процессоры. Ранее все программы писались под одноядерные архитектуры и, для того, чтобы использовать всю мощность многоядерных систем программу необходимо переписать. К чем это я ? Несмотря на то что Scala как и Java императивный язык, он поддерживает написание программ в функциональном стиле. А Clojure полностью функциональный язык, основная идея которого взята с языка Lisp. Посмотрим на список плагинов нашего любимого Play! и с радостью находим там поддержку Scala.

И так, хватит уже заниматься болтавней давайте приступим к установке фреймворка!
Моя рабочая система Ubuntu, все примеры я буду показывать на ней. Я всегда люблю использовать самые последние версии программного обеспечения, поэтому ставить Play! мы будем из исходников. Более простая установка описана на офф.сайте фреймворка, но мы пойдем другим путем и чуть ниже я объясню почему.

Нам нужен установленный git. В ubuntu это просто:
$ sudo apt-get install git
Создадим директорию для наших экспериментов, пусть это будет ~/dev
$ mkdir ~/dev
$ cd ~/dev
Получим последнюю версию исходников Play! с GitHub:
$ git clone https://github.com/playframework/play.git
Соберем Play! Для этого нам будет нужна утилита для сборки java-проектов, ant.
Установим:
$ sudo apt-get install ant
Хочу заметить, что я не использую для работы OpenJDK. Поэтому смотрите внимательно на зависимости, которые будет тянуть за собой пакет. Если у вас корректно в системе установлена Sun/Oracle Java SDK, то проблем не будет.
Переходим в директорию для сборки Play!
$ cd play/framework
Запускаем сборку проекта
$ ant

Отлично, теперь поставим недостающие плагины из исходников)
Переходим в директорию с плагинами Play!:
$ cd ~/dev/play/modules
Скачиваем исходный код Scala плагина:
$ git clone https://github.com/guillaumebort/play-scala.git
Скачиваем исходный код Google App Engine плагина:
$ git clone https://github.com/guillaumebort/play-gae.git
Скачиваем исходный код Siena плагина:
$ git clone https://github.com/vijaykiran/play-siena.git

Теперь приступим к сборке. Для начала создадим символические ссылки для удобства:
$ ln -s play-scala scala
$ ln -s play-gae gae
$ ln -s play-siena siena

Собираем Scala плагин:
$ cd scala
$ ant -Dplay.path=~/dev/play

Собираем GAE плагин:
$ cd ../gae
$ ant -Dplay.path=~/dev/play

Собираем Siena плагин:
$ cd ../siena
$ ant -Dplay.path=~/dev/play

Ура! Уже теперь Play! самый свежий и готов к работе. Создадим директорию для наших Play! проектов:
$ mkdir ~/src
$ cd ~/src

Создадим Play! проект с поддержкой Scala, назовем его TestApp
$ ~/dev/play/play new TestApp --with scala

И сразу его запустим)
$ ~/dev/play/play run TestApp

А теперь про модули. Для чего мы ставили Siena ? Прежде всего для удобства разработки, т.к. Siena предоставляет одинаковый интерфейс для работы с хранилищами данных SQL, NoSQL, etc. Т.е. нам не нужно будет переписывать слой работы с данными если мы вдруг захотим сменить хранилище.

Теперь давайте укажем Play! использовать наши плагины, для этого отредактируем application.conf файл нашего проекта:
$ vim TestApp/conf/application.conf

В секции "Additional modules" допишем 3 строчки:
module.scala=${play.path}/modules/scala
module.siena=${play.path}/modules/siena
module.gae=${play.path}/modules/gae

Это пожалуй все по настройке приложения для работы связки Play! + GAE + Siena.

P.S. я обещал написать почему все ставим из исходников. Во время разработки я сталкивался с некоторыми проблемами работы плагинов. К примеру ScalaCache в production на GAE использовал Cache на Actors, вместо GAE Cache. По этой причине приложение "кушало" много ресурсов и вылетало с ошибкой за все бесплатные ограничения App Engine. Проблема была оперативно решена разработчиком плагина, но в офф.репозитарии Play! эта версия плагина еще не появилась. Или вот еще. Siena не могла корректно сохранять Blob поля в GAE. Разработчик "пофиксил" плагин, но в офф.репозитарии Play! свежей версии не было. Время шло, проект надо сдавать. Сборка всего из исходников решила проблему. В следующей статье опишу приёмы разработки на Play! + Scala.

Удачи!



12 дек. 2010 г.

JDK,Scala & IDEA 10 & Ubuntu 10.10 How To ?

Не так давно я решил некоторые проблемы со своим ПК, и наконец-то выделил дисковое пространство для ubuntu 10.10. После установки системы я начинаю подготавливать рабочую среду. По своему роду деятельности мне приходится разрабатывать гибридные .NET/Java проекты. Первым делом на новой системе мне нужно установить JDK. К сожалению по умолчанию в ubuntu 10.10 идет OpenJDK. Это нам совершенно не подходит. Приступаем к установке Sun/Oracle JDK.

1.Смотрим что у нас установлено из OpenJDK:
sudo dpkg --get-selections | grep openjdk

2.Удаляем все пакеты с OpenJDK:
sudo apt-get remove openjdk-6-jre-headless

По идее можно сделать так:
sudo dpkg --get-selections | grep openjdk > sudo apt-get remove, но я не пробовал

3.Ставим Sun/Oracle JDK:
sudo add-apt-repository ppa:sun-java-community-team/sun-java6
sudo apt-get update
sudo apt-get install sun-java6-jdk

4.Проверяем версию Java:
$ java -version
java version "1.6.0_21"
Java(TM) SE Runtime Environment (build 1.6.0_21-b06)
Java HotSpot(TM) Client VM (build 17.0-b16, mixed mode, sharing)

Теперь скачаем последнюю версию Scala. Смело идем на scala-lang.org и качаем.
Я ставлю Scala в каталог /opt/scala, и прописываю пути до него в PATH

Далее качаем последнюю версию IDEA Community Edition. На момент написания статьи была версия 10.0. Распаковываем архив куда вам угодно и запускаем IDEA.
Сразу идем File - Settings - Plugins и ставим свежий плагин Scala (не Scala Power Pack).
Перезапускаем IDEA и можно уже создавать Scala проекты.
Еще ремарка, в Run configurations выбираем шаблон Application, указываем главный класс и проект. Основной класс программы должен реализовывать def main(args: Array[String]), пример:

object App {
def main(args: Array[String]) {
println("Hello world!")
}
}

8 апр. 2010 г.

Dependency Injection на примере с Autofac

Доброе время суток!
В этом посте мы на примере разберем понятие dependency injection (внедрение зависимости).
Для начала хочу рассказать по своему опыту. У меня был проект, довольно сложный. В погоне за правильной архитектурой приложения я разбил проект на модули. Проект заработал и был запущен в production. Все бы было хорошо, пока дело не дошло до внесения изменений в функционал/код. Большие зависимости между классами вызывали трудности при тестировании и внесении изменений в код. На моей электронной полке к тому времени уже давно лежала книжка Dhanji R. Prasanna Dependency Injection.
Т.к. проект у меня был на .NET я начал искать IoC контейнеры в реализации для этой платформы.

Найдены были:
Почему Windsor на первом месте ? :) Потому что как-то давно, я уже делал на нем простые примеры. Но сейчас мой выбор пал на AutoFac. Аргументы ? Во-первых проект очень быстро развивается. Уже сейчас, еще до выхода .NET Framework 4.0, уже есть поддержка последнего фреймворка. Это не может не радовать! Во-вторых уже сейчас в AutoFac есть поддержка WCF, MEF, ASP.NET MVC. Поддержку MEF выделяю особенно. Ну и в-третьих мне понравилась архитектура библиотеки, все ясно и понятно.

И так, рассмотрим работу AutoFac на небольшом примере. Для начала создадим консольный проект. В этом же solution создадим class library и назовем её IfaceLib. В этой библиотеке опишем 2 интерфейса.

public interface ILibOne
{
string getMyName();
}
public interface ILibTwo
{
string getMyFullName();
}

Теперь создадим еще одну class library и назовем её LibOne. В ней мы реализуем интерфейс ILibOne в классе LibOneClass. Перед этим не забудем зареференсить сборку IfaceLib.

public class LibOneClass : ILibOne
{
public string getMyName()
{
return "Spectrum";
}
}

Создадим еще одну class library и назовем её LibTwo. В ней мы реализуем интерфейс ILibTwo в классе LibTwoClass.

public class LibTwoClass : ILibTwo
{
ILibOne one;

public LibTwoClass(ILibOne one)
{
this.one = one;
}

public string getMyFullName()
{
return "ZX " + one.getMyName();
}
}

Как мы видим LibTwoClass явно использует метод getMyName() класса LibOneClass через интерфейс. Но так же LibTwoClass ничего не подозревает, о существовании LibOneClass. Этого мы и добивались.
Теперь приступим к реализации самой программы, использующей эти классы.

class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<LibOneClass>().As<ILibOne>();
builder.RegisterType<LibTwoClass>().As<ILibTwo>();
builder.Register(c => new LibTwoClass(c.Resolve<ILibOne>()));
var container = builder.Build();

var two = container.Resolve<ILibTwo>();
Console.WriteLine(two.getMyFullName());
Console.ReadLine();
}
}

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

var builder = new ContainerBuilder();

Регистрируем типы:

builder.RegisterType<LibOneClass>().As<ILibOne>();
builder.RegisterType<LibTwoClass>().As<ILibTwo>();

Далее мы определяем поведение контейнера, при создании LibTwoClass. Ведь в этом классе есть зависимость от LibOneClass. В следующей строке мы указываем контейнеру, как ее разрешить:

builder.Register(c => new LibTwoClass(c.Resolve<ILibOne>()));

Таким образом, при создании LibTwoClass контейнер автоматически разрешит зависимость, и подставит заместо ILibOne экземпляр LibOneClass.
В результате выполнения программы на экране мы получим:

ZX Spectrum

Что нам дает использование IoC контейнера ? Мы можем писать самодостаточные классы без разрешения зависимостей между ними. Мы можем проводить независимое юнит-тестирование каждого класса. Мы можем безболезненно вносить изменения в класс.

На этом у меня пока все. В следующем топике постараюсь рассказать про использование mock-объектов.

9 янв. 2010 г.

Corel Draw X3 на Ubuntu 9.10


Всем хороша Ubuntu 9.10. Но на одном из проектов возникла острая необходимость работать с Corel Draw. В такой ситуации есть 3 выхода:
1. Использовать Corel Draw на родной Windows платформе.
2. Установить Windows в Sun Virtual Box и туда же Corel Draw
3. Попытаться запустить Corel Draw через WINE.

Первый вариант не подходил изначально, т.к. это постоянные перезагрузки ПК для работы то в Windows, то в Linux. Второй вариант утомителен. Установка операционной системы в виртуальную машину только для того, что бы запустить Corel Draw. Третий вариант... Остался только он. Сразу же скажу, что с WINE я практически не работал. На рабочей Linux машине стоит WINE версии 1.0.1 из репозитария Ubuntu. Самый верный способ избежать возни с версиями dll это скачать portable версию продукта, что я и сделал. И так на машине имеем распакованную portable версию Corel Draw X3. Переходим в корневой каталог программы и пишем магическую команду:
wine ./CorelDRW.exe

Попытка запуска не удалась. Не хватает библиотеки mfc42.dll. Библиотека была успешно скопирована с рабочей Windows в папку с Corel Draw. Повторно набираем магическую команду, и вуаля! Corel Draw запущена.

Некоторые замечания по работе. Прорисовка окон будет заметно быстрее если в корневую папку программы вы дополнительно скопируете библиотеки:
mfc42loc.dll, mfc42rus.dll, mfc42u.dll

Corel Draw успешно падает при слишком быстром прокручивании списка шрифтов. Со шрифтами стоит быть осторожнее. Буфер обмена так же не работает. Поэтому объекты копировать не получится.

Это пока основное что я заметил.

25 окт. 2009 г.

Инструменты

Пожалуй расскажу про инструменты, с виду и не очень заметные, но которыми я пользуюсь каждый день. Это FAR, Thunderbird, Evernote...
Раньше, где-то год назад я был ярым поклонником Total Commander. Помню у меня всегда была под рукой portable сборка тотала. Но как в 2007 году устроился на новую работу, так со временем Total Commander пропал из моих Top 10 инструментов. Мой начальник пользовался сборкой Far 1.7. Т.к. наши ПК стоят рядом, я частенько видел синие окна Far'а у него на мониторе. Я этого не понимал. Казалось бы на дворе 21 век, технологии идут вперед, а мы тут в DOS окнах сидим. Но что меня еще больше удивляло, дак это скорость, с которой мой начальник управлялся с файлами. Прошло какое-то количество времени, и я решил все таки установить себе Far. Total Commander удалять и не собирался:) Пользовал оба инструмента, так же под Far установил плагины по совету начальника. Первое что меня удивило, это скорость открытия Far. Интуитивно понятный интерфейс. Возможность настраивать Far полностью под себя, любые мелочи. Я тут же прицепил туда свою любимый архиватор 7-Zip, кстати его тоже можно отнести к моим Top 10 инструментам. Самое главное, это подсветка синтаксиса исходных текстов. На работе очень часто приходится иметь дело с VBS-сценариями, Far'ом их править очень удобно. Учитывая то что сценарии, разбросаны по разным директориям, сетевым дискам. И это далеко не все возможности FAR'a! Подсветка синтаксиса множества языков, и еще куча приятных мелочей. Другими словами работу на ПК без FAR я уже просто не представляю возможной.
Теперь про почту... Наверное она уже есть у каждого, и может даже не одна. Рабочая почта, внешняя, вторая и как их там еще называют. У меня их тоже две) Живу я больше во второй, ибо она в сотни раз удобнее первой, потому что это GMail! Но письма приходится просматривать с обеих. Конечно можно пользоваться WEB-интерфейсом, но с возрастом хочется все меньше делать движений мышью) Старею) Из все представленных бесплатных почтовых клиентов, мой выбор пал на Thunderbird. Симпотичный клиент, с минимальными возможностями. Поддерживает POP3/IMAP в принципе то что надо. Почту я смотрю всегда через IMAP и не забираю ее с сервера. По заголовкам можно быстро отфильтровать прорвавшийся спам, и удалить прочитанные письма. Одним словом все что нужно.
С недавного времени я завел аж целых 2 блога, наполнять их пытаюсь по возможности. Статьи в блог можно конечно формировать прямо на сайте блога, но это не всегда удобно. Привычнее все равно будет настольное приложение, но с сетевыми возможностями. Я выбрал Evernote... Таким образом я могу начать писать статью дома, а закончить ее на работе или у друзей в гостях. Evernote очень быстро синхронизирует данные, так же есть интеграция с сервисами "Все ли сделал!" и подобными. Удобно и просто.
Пока я описал только эти инструменты. В следующих статьях я подробно постараюсь описать работу в MS Visual Studio и Eclipse, так уж повелось, что я пишу и на C# и на Java. Удачи!

27 авг. 2008 г.

UnitOfWork & IdentityMap

Решил и я выложить свой взгляд на эти вещи. Много документации было прочитано, много ресурсов было найдено. Прежде всего UnitOfWork понимает с дословного перевода "Единица работы". Т.е. это определенный блок команд/операций которые должны буду выполнены за один цикл. Этот цикл будет рассматриваться как одна операция (транзакция в СУБД).

Теперь собственно код:

///
/// Класс-контейнер UnitOfWork
///

public sealed class UnitOfWork
{
private List _items;

///
/// Конструктор
///

public UnitOfWork() {
log = new CLog();
_items = new List();
}

///
/// Добавляем новый объект в контейнер и помечаем его как новый
///

public void New(DomainObject obj)
{
foreach (DomainObject sobj in _items
.Where(f => f.objectState == ObjectState.NewObject))
{
if (sobj.GUID == obj.GUID)
{
log.Add("Объект уже зарегестрирован как новый => " + obj.GetType().Name.ToString());
return;
}
else
{
obj.objectState = ObjectState.NewObject;
_items.Add(obj);
return;
}
}

obj.objectState = ObjectState.NewObject;
_items.Add(obj);
}

///
/// Добавляем новый объект в контейнер и помечаем его как измененный
///

public void Dirty(DomainObject obj)
{
foreach (DomainObject sobj in _items
.Where(f => f.objectState == ObjectState.DirtyObject))
{
if (sobj.GUID == obj.GUID)
{
log.Add("Объект уже зарегестрирован как измененный => " + obj.GetType().Name.ToString());
return;
}
else
{
obj.objectState = ObjectState.DirtyObject;
_items.Add(obj);
return;
}
}

obj.objectState = ObjectState.DirtyObject;
_items.Add(obj);
}

///
/// Добавляем новый объект в контейнер и помечаем его как удаленный
///

public void Removed(DomainObject obj)
{
foreach (DomainObject sobj in _items.Where(f => f.objectState == ObjectState.DeletedObject))
{
if (sobj.GUID == obj.GUID)
{
log.Add("Объект уже зарегестрирован как удаленный => " + obj.GetType().Name.ToString() + " [" + obj.UID.ToString() + "]");
string o = "Obj => " + obj.GUID.ToString();
string o1 = "Sobj => " + sobj.GUID.ToString();
log.Add(o);
log.Add(o1);
}
else
{
obj.objectState = ObjectState.DeletedObject;
_items.Add(obj);
return;
}
}

//если IM пустая
if (_items.Count == 0)
{
obj.objectState = ObjectState.DeletedObject;
_items.Add(obj);
}
}

public void Clear()
{
_items.Clear();
}

public IList Get()
{
return _items;
}
}