Я не устоял перед соблазном и решил попробовать запустить JRuby on Rails в AppEngine. На этот шаг меня вдохновил целый ряд постов в разных блогах и мое повествование будет неполным без них:
- JRuby on Rails on Google App Engine
- Running Sinatra apps on Google AppEngine (Java)
- JRuby on Google AppEngine: First Impressions
- JRuby on Rails Google App Engine, жаль язык автора остался для меня загадкой
По правде, до этого пользоваться JRuby мне приходилось только один раз, да и то, ради любопытства. Думаю многим JRuby знаком так же “хорошо”, поэтому начнем с самого начала. А для начала нам понадобиться свежий JRuby. Вытягиваем его из репозитория и собираем:
svn co http://svn.codehaus.org/jruby/trunk/jruby
cd jruby
ant && ant jar-complete
Использовать rake build тоже можно, но результатом его работы будет только собраный JRuby. А нам с вами нужен еще jruby-complete.jar, чуть позже мы вернемся к нему. Пока же для удобства пропишите себе путь к jruby/bin и продолжим – установим Rails и сделаем скелет приложения:
jruby -S gem install rails
jruby -S rails depot
Замораживаем все gems и удаляем в них тесты. Как я уже писал, GAE/J имеет ряд ограничений, в том числе и на число файлов. Их количество не должно привышать 1000 штук. Надеюсь, это временно ограничение. ActiveRecord нам тоже больше не нужен, на GAE/J применения ему нет, за компанию выбросим и ActionMailer с ActiveResource:
jruby -S rake rails:freeze:gems
rm -rf vendor/rails/actionpack/test/
rm -rf vendor/rails/activerecord/
rm -rf vendor/rails/actionmailer/
rm -rf vendor/rails/activeresource/
Поскольку от ActiveRecord, ActionMailer и ActiveResource мы избавились, будет разумным сразу поправить config/environment.rb:
environment.rb
config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
Пока оставим Rails и займемся SDK для Google AppEngine Java. Для начала, вам следует скачать его с code.google.com, после разверните его в удобное для вас место и пропишите пути к скриптам в appengine-java-sdk/bin.
Из поставки GAE/J SDK нам понадобиться библиотека appengine-java-sdk/lib/impl/appengine-api.jar. Скопируйте ее в RAILS_ROOT/lib. Туда же нужно будет положить jruby-complete.jar из jruby/lib и jruby-jrack.jar. Последний можно найти в репозитории jruby-rack:
git clone git://github.com/nicksieger/jruby-rack.git
cd jruby-rack
jruby -S rake SKIP_SPECS=true
Поскольку jruby-complete.rb слишком большой для GAE/J, воспользуемся скриптом Ola Bini, позволяющим разбить его на две части – jruby-core.jar и ruby-stdlib.jar:
fix_libs.sh
#!/bin/sh
rm -rf jruby-core.jar
rm -rf ruby-stdlib.jar
rm -rf tmp_unpack
mkdir tmp_unpack
cd tmp_unpack
jar xf ../jruby-complete.jar
cd ..
mkdir jruby-core
mv tmp_unpack/org jruby-core/
mv tmp_unpack/com jruby-core/
mv tmp_unpack/jline jruby-core/
mv tmp_unpack/jay jruby-core/
mv tmp_unpack/jruby jruby-core/
cd jruby-core
jar cf ../jruby-core.jar .
cd ../tmp_unpack
jar cf ../ruby-stdlib.jar .
cd ..
rm -rf jruby-core
rm -rf tmp_unpack
rm -rf jruby-complete.jar
Этот скрипт можно найти в репозитории Ola Bini YARBL. Для замены ActiveRecord нужно будет установить Bumble из репозитория. Добавьте загрузку Bumble в environment.rb. В enviroments/session_store.rb отключите сессии. Готового решения для Rails 2.3.2 пока нет (узнать подробней об этом можно здесь). Пользоваться cookie для хранения сессий пока нельзя, нет обвязок для java.security и javax.crypto.
Библиотеки все собраны и для запуска нам осталось совсем немного. Установите warbler и сконфигурируйте его. Делать это надо в RAILS_ROOT и обратите внимание на коварство разработчиков – gem называется warbler, а скрипт warble:
jruby -S gem install warbler
jruby -S warble pluginize
jruby -S warble config
Конфигурация находиться в config/warble.rb:
warble.rb
config.includes = FileList["appengine-web.xml", "datastore-indexes.xml"]
config.webxml.jruby.min.runtimes = 1
config.webxml.jruby.max.runtimes = 2
Файлы appengine-web.xml и datastore-index.xml стандартные для приложений GAE/J. В первом доступна конфигурация приложения, во втором – индексы для DataStore. В нашем случае мы воспользуемся самым простыми вариантами этих двух файлов:
appengine-web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>depot</application>
<version>2</version>
<static-files />
<resource-files />
<sessions-enabled>true</sessions-enabled>
<system-properties>
<property name="jruby.management.enabled" value="false" />
<property name="os.arch" value="" />
<property name="jruby.compile.mode" value="JIT"/> <!-- JIT|FORCE|OFF -->
<property name="jruby.compile.fastest" value="true"/>
<property name="jruby.compile.frameless" value="true"/>
<property name="jruby.compile.positionless" value="true"/>
<property name="jruby.compile.threadless" value="false"/>
<property name="jruby.compile.fastops" value="false"/>
<property name="jruby.compile.fastcase" value="false"/>
<property name="jruby.compile.chainsize" value="500"/>
<property name="jruby.compile.lazyHandles" value="false"/>
<property name="jruby.compile.peephole" value="true"/>
</system-properties>
</appengine-web-app>
datastore-indexes.xml
<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes autoGenerate="true">
</datastore-indexes>
Теперь можно попробовать запустить тестовый сервер GAE/J:
jruby -S warble
dev_appserver.sh tmp/war
Если все в порядке, то по адресу http://localhost:8080 вам будет доступен тестовый сервер GAE/J с вашим приложением.