Riding JRuby on Rails on SAP NetWeaver Cloud: the cloud case
Overview
Last time we created a simple photo management web application in Rails, using a JRuby environment, and ran it on a local server. In this blog we will show you how to port it to run on top of SAP NetWeaver Cloud as a production environment. The application will make use of several NW Cloud services that will power its functionality. This tutorial could generally serve as a NW Cloud migration guide for other Rails applications as well. The first part if the series is a good preparation for our cloud journey here but is not mandatory for understanding this guide.
What is going to change
In general, the business logic and the UI of our application will stay the same. We will focus on the back-end resources that the app uses.
In the local development scenario the photos that we upload are stored directly on the file system. We could run that on NetWeaver Cloud as well, but in a cloud environment this is certainly not a good design. We need a reliable storage solution which will be 24/7 available and will prevent our pictures from being lost, in case the application VM crashes and is replaced with a new one by the NW Cloud infrastructure. For this purpose we are going to use the Document Service in SAP NetWeaver Cloud.
Furthermore, we have a relational database usage for our ActiveRecord models, powered by an SQLite3 database file, which lives again on the file system. For the same reasons this is not acceptable and needs a replacement. We will use the Persistence Service in NW Cloud for this functionality: it provides with relational database storage, powered by the MaxDB platform and offered by JDBC and JPA entry points.
Basically we will start with the application as we left it last time – the sources are in our project’s GitHub repo, in the local branch. The changed application sources that this tutorial will produce at the end are available there as well, in the no-dest branch (the reasons for this strange name are historical). If you get something unclear here you can always reach out to it as a reference, or better ask us.
Getting Started
We will need some additional gems for flying our application on SAP NetWeaver Cloud. To use the persistence service of NW Cloud our application needs MaxDB support for ActiveRecord. So let’s install those two gems: jdbc-maxdb and activerecord-maxdb-adapter (if you don’t have a Git installation, you can download the ZIPped repositories from GitHub directly):
jruby -S gem build jdbc-maxdb.gemspec
jruby -S gem install jdbc-maxdb
jruby -S gem build activerecord-maxdb-adapter.gemspec
jruby -S gem install activerecord-maxdb-adapter
Our app will need a different Gemfile for the production environment, so we use this one and run Bundler against it:
jruby -S bundle update
It will install the Warbler gem that will transform our Rails app into a WAR archive, suitable for running on NW Cloud. We create a Warbler configuration:
jruby -S bundle exec warble config
Document Service
Using the ECM service for storing our photos will be achieved with the help of a tiny adapter layer, written in Java. You can find it in the cmis_client folder. Let’s go through the steps needed to incorporate it into our application. You will see that those steps are generic enough to be reused by other applications, possibly at the price of small modifications.
In essence, we are going to use our own ECM repository. The parameters for this usage that you can adjust to your preference are repository key, repository unique name and user name. You can find them in the CMISProxyServlet and CmisClient classes.
File system storage is not longer our choice, so we delete the uploaded folder. First we have to compile the Java classes in this adapter layer. For that we have to download locally SAP NetWeaver Cloud SDK from here, and supply the file system path to it (after getting unzipped) inside the compile.rb helper script. Next, we execute the compilation:
mkdir classes
jruby compile.rb
We put the CmisClient class in a separate jar:
cd classes
jar -cf cmis_client.jar cmis/client/*.*
And finally we have to produce our WAR. But first, we will need to pre-compile application’s assets so that they make it into the WAR and are thus available in the production environment. This will take some time, so be patient (we must make sure that the production environment here still points to SQLite3, as opposed to the GitHub file which is already gone through the tutorial):
jruby -S rake assets:precompile
jruby -S bundle exec warble
This will generate the WAR file, including the web.xml descriptor inside. Please add the generated cmis_client.jar in WEB-INF/lib folder of the WAR, and the compiled CMISProxyServlet class in WEB-INF/classes, including all of its package-subfolders, that is, the com/sap/photo/app/CMISProxyServlet.class. In our case we have added some lines in the config/warble.rb configuration file to ask Warbler to do that for us automatically.
The next part comes a little bit boring, although its just a copy-paste-edit exercise: we incorporate the changes described in the photoapp/web.xml-instructions file into our web.xml descriptor: declaring the CMISProxyServlet and its mapping, modifying the URL mapping for the RackFilter, and adding the security section. It’s a good idea to keep this web.xml because we’ll need it later.
Persistence Service
We configure our config/database.yml file to use the MaxDB adapter for NetWeaver Cloud:
production:
adapter: maxdb
encoding: utf8
reconnect: false
jndi: java:comp/env/jdbc/DefaultDB
pool: 5
and add in the config/warble.rb file this JNDI reference:
config.webxml.jndi = 'jdbc/DefaultDB'
In order to be able to execute our database migrations in NW Cloud environment, we instruct Warbler to include them in the WAR package that it produces, by adding the db folder in the following line in config/warble.rb (note that the public folder is included as well: it’s there for the pre-compiled assets):
config.dirs = %w(app config lib db log vendor public tmp public)
and we add to our application a new initializer jpaas_db_migrate.rb in the config/initializers folder.
Finally, we prefix our table names as adding the following line in config/application.rb, which is needed because of MaxDB specifics:
config.active_record.table_name_prefix = 'r'
And if you suspect that we now have to warble our app again in order to incorporate those changes, you are right: we run it again, and we must be careful to not forget the changes from the previous section: packaging the proxy servlet, the cmis_client jar and modifying the web.xml
Adapting the application to the changes
We apply some changes in order to adapt the application to the ECM usage (the database level switch is transparent to the app and does not require any modifications): take a look at how the cmis client is being consumed from the Photo model, controller and view.
Running the application on the cloud
That was it! We are now ready to fasten our seat belts and take off! Let’s deploy the application:
and start it. We access the app in the browser again at the known addresses https://photoappi055691trial.hanatrial.ondemand.com/photoapp/albums and https://photoappi055691trial.hanatrial.ondemand.com/photoapp/photos, to see for ourselves that storing fruits in the cloud has never been easier:
Cheers!
The last part of these blogs will show you usage of Connectivity Service and will present with some details about the Rails integration, for those who are interested about how all this really works.
Krum, thanks for this great tutorial!
Is it possible to publish the MaxDB gems on rubygems.org? This will save two steps that are currently required: fetching them from GitHub and building them locally.
Thanks, Kaloyan, for the feedback. Yes, I also think that we should make the gems available on the central repository. I will take a look for how to integrate that.
Way to go... !!! 🙂
Great tutorial, thank you!
If you have git correctly configured on your machine, it is sufficient to just add the following lines in your GEMFILE. No need to build the gems manually:
gem
'jdbc-maxdb', '~> 7.8.00', :git =>
'https://github.com/sapnwcloudlabs/jdbc-maxdb-gem.git'
gem 'activerecord-maxdb-adapter', '~> 0.2', :git =>
'https://github.com/sapnwcloudlabs/activerecord-maxdb-adapter.git'
Also, put the following line into your application.rb to prevent running the migration in the production environment every time you precompile your assets:
config.assets.initialize_on_precompile
= false # NW Cloud requires migrations to be run in initializer on app start -> this would fail in precompilation!
Thanks, Enrico, for your remarks and comments! You are right, Gemfile supports this Git-based declarations (you can even specify branch for the Git repo).
Nevertheless, integrating the gems on the official gem repository would be the cleanest way to provision them to users.
And you are perfectly right for the precompilation optimization that you mention: this would eliminate the need to always pay attention for the adapter settings (sqlite3/maxdb), and would probably shorten this part of the tutorial, and make it easier to follow.
For debugging your Rails application on NetWeaver Cloud, one approach would be to add the following lines inside your config/environments/production.rb file:
config.log_level = :debug
config.logger = Logger.new (STDOUT)
This will make the logs appear inside the standard ljs_trace_* log file.
Dear blog readers,
We have just introduced a small refactoring of the contents of this blog. The scenario described here is now a little bit shorter and simpler, and the application sources can be found in the no-dest branch. The application functionality stays unchanged. (in short, the document service integration is simplified)
The sources for the old approach are still available in the master branch, but this approach is no longer recommended.
Cheers,
Krum.
Short Update:
The somewhat odd requirement to add prefix to the table names is no longer needed. If you take the latest adapter from the GitHub repo, it will work without this step.
Hi Krum,
Is it still fine to use the MaxDB adapter if we're using HANA Cloud Platform with HANA DB? I remember the JPA setup in Eclipse for HANA DB was based on a MaxDB provider so possibly this is the same...
Cheers,
Scott
Hi Scott,
In general you're guessing it right here: at least in the beginning of HANA DB, its SQL dialect was pretty similar to the one of MaxDB. So with regards to the adapter: there is a good chance that most of the MaxDB adapter will work unmodified (or with small changes) against HANA DB. However, bear in mind that some configuration details would probably fail to work if not adjusted properly for HANA, like JDBC connection URL, default port, JDBC driver name, etc.
There are already plans in the community for extending the JRuby initiative to cover HANA as well, as the next logical contribution step into this area. At the current moment I couldn't really give you any particular timelines for this, though.
Best Regards,
Krum.