Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 

I'm a big fan of Grails and I've been using it since the early days. The built-in scaffolding functionality is really great and speeds up my development time by providing out-of-box CRUD pages without having to write a bunch of boilerplate codes. One thing that you'll notice is that the UI generated by the scaffolding is very simple considering that we are now surrounded by tons of HTML5 and jquery-based UIs.

Recently, SAP launched the SAP NetWeaver Cloud and it's accompanying UI5 library for generating nice-looking UI. I've started to play around with it and I decided to modify the default scaffolding templates of Grails so that it uses the SAP UI5 to generate the CRUD pages.

Prerequisites

Before we begin, there are stuff that you need to download and install first:

To get a trial account for SAP NetWeaver Cloud click here: https://help.netweaver.ondemand.com/default.htm?trial_account.html

Let's Get Started

Assuming that you've already installed the prerequisites above, we can start by creating our Grails project:

grails create-app FooApp
cd FooApp

Next, for us to be able to take advantage of the SAP NetWeaver Cloud Deployment Plugin for Maven, we need to mavenized our project:

grails create-pom com.sap

This will create the pom.file for us that includes all the necessary dependencies with the groupId set to com.sap. Then we need to add our NW Cloud Plugin to the list of plugins in pom.xml:

<plugin>
            <groupId>com.sap.research</groupId>
            <artifactId>nwcloud-maven-plugin</artifactId>
            <version>1.0.0.RELEASE</version>
            <executions>
                <execution>
                    <id>after.package</id>
                    <phase>package</phase>
                    <goals>
                        <goal>hint</goal>
                    </goals>
                </execution>
            </executions>
</plugin>

Then try to do a mvn clean compile to test our setup. If you see BUILD SUCCESS at the end then we can move on to the next part which is to setup our datasource.

Datasource

I want to be able to use the SAP NetWeaver Cloud's built-in persistence service. There are several ways that we can connect to the persistence service but in this case, we don't really need to care because Grails will handle it for us. We just need to declare the JNDI name in our grails-app/conf/DataSource.groovy and update web.xml to reflect the JNDI resource as well.

grails-app/conf/DataSource.groovy

production {
        dataSource {
                              dbCreate = "create"
                           jndiName = "java:comp/env/jdbc/DefaultDB"
                               dialect = "org.hibernate.dialect.SAPDBDialect"
        }
    }

You'll notice that we only declare the jndi datasource for the production mode because we still want to take advantage of the grails' in-memory database when in development mode.

Declaring the JNDI name in web.xml requires a bit of effort because web.xml is also being generated automatically for you by Grails. What we need to do is to listen for the event when Grails is done generating our web.xml and modify it to append our jndi resource. To do this, you need to create the _Events.groovy in scripts folder.

scripts/_Events.groovy

eventWebXmlEnd = { String filename ->
          def webxml = webXmlFile
          def newxml = new File(webxml.path + ".bak")
          def root = new XmlParser().parse(webxml)
          // add the jdbc/mydatasource resource reference to the web.xml
          def resourceRef = root.appendNode('resource-ref')
          resourceRef.appendNode('description','The SAP NetWeaver JNDI Database resource')
          resourceRef.appendNode('res-ref-name','jdbc/DefaultDB')
          resourceRef.appendNode('res-type','javax.sql.DataSource')
          def writer = new StringWriter()
          new XmlNodePrinter(new PrintWriter(writer)).print(root)
          newxml.withWriter { out ->
                    out.writeLine(writer.toString())
          }
          webxml.delete()
          newxml.renameTo(webxml.path)
}

Here, we are appending the resource-ref node to the end of web.xml and finally replacing the original file with the modified one.

Lastly, we need to copy a jar file from the NW Cloud SDK to our lib folder. Go to $NW_CLOUD_SDK/repository/plugins/com.sap.security.core.server.csi_1.0.1.jar and copy this into your lib folder and we're done with our data source configuration. Now let's move on to create our first model.

Model

In this example, we are just going to create a very simple model Person with few properties.

grails create-domain-class com.sap.fooapp.Person

This will generate grails-app/domain/com/sap/fooapp/Person.groovy. Open that file and add the properties to make it look like below:

package com.sap.fooapp
class Person {
          String firstName
          String lastName
          String department
          String notes
     static constraints = {
                    firstName blank:false, size: 2..20
                    lastName blank:false, size: 2..20
                    department inList: ["Sales", "Development", "HR"]
                notes widget:'textarea'
    }
}

Let's generate the controller and views for our model and do our first run so that we can test if everything is still ok.

grails generate-all com.sap.fooapp.Person
grails run-app

And if you leave the default port to 8080, fire up your browser and open http://localhost:8080/FooApp and you should see the index page of your Grails app. You can open http://localhost:8080/FooApp and play around with the crud pages before we actually replace everything with UI5.

Templates

Now we need to install the templates used by Grails to generate the default views. Basically, we want to be able to  generate the default CRUD pages that uses UI5 library. Let's install the templates by executing:

grails install-templates

Several files will be added to the folder src/templates. Our target is to replace the files inside src/templates/scaffolding. Extract this zip file to this folder and overwrite all the files.

Then let's generate our views and controllers again for our Person model and overwrite all files.

grails generate-all com.sap.fooapp.Person

Next, let's modify our main layout file to include our SAP UI5 Javascript Library. Open grails-app/views/layouts/main.gsp and add the following line under the <head> section and just before <g:layoutHead/>:

<script id="sap-ui-bootstrap" type="text/javascript"
src="https://sapui5.netweaver.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-theme="sap_goldreflection"
data-sap-ui-libs="sap.ui.commons,sap.ui.table"></script>

Then we're ready to test it out:

grails run-app

Browse http://localhost:8080/FooApp/person/ and if everything is good, you should see something like:

Try to add a new person and you can edit or delete an item by clicking on the far left side space in each row.

NOTE: The templates provided only supports String types for now but hopefully I may find time to add support for other types as well such as Date, boolean, Integers as well as associations.

Now we're ready to deploy to the cloud!

Deployment to the Cloud

Issues with Manifest file

Grails automatically generates the WEB-INF/META-INF/MANIFEST.MF but it seems the NetWeaver cloud isn't happy with the values present in the manifest file and good thing I stumbled upon this article by Jakub Sendor. Bascially the idea is to have a default manifest file that contains just a manifest version and use that to replace the one generated by Grails. Again, we can do this from the _Events.groovy by listening to the event when the creation of war file is going to start. Open up your scripts/_Events.groovy again and it should look something like:

includeTargets << grailsScript("_GrailsInit")
eventCreateWarStart = { warName, stagingDir ->
    ant.copy(todir: "$stagingDir/META-INF", file: "web-app/META-INF/MANIFEST.MF", overwrite: true)
}
eventWebXmlEnd = { String filename ->
          def webxml = webXmlFile
          def newxml = new File(webxml.path + ".bak")
          def root = new XmlParser().parse(webxml)
          // add the jdbc/mydatasource resource reference to the web.xml
          def resourceRef = root.appendNode('resource-ref')
          resourceRef.appendNode('description','The SAP NetWeaver JNDI Database resource')
          resourceRef.appendNode('res-ref-name','jdbc/DefaultDB')
          resourceRef.appendNode('res-type','javax.sql.DataSource')
          def writer = new StringWriter()
          new XmlNodePrinter(new PrintWriter(writer)).print(root)
          newxml.withWriter { out ->
                    out.writeLine(writer.toString())
          }
          webxml.delete()
          newxml.renameTo(webxml.path)
}

Then create the dummy manifest file in web-app/META-INF/MANIFEST.MF and just write

Manifest-Version: 1.0

:

Cloud properties

Next, we need to create nwcloud.properties at the root of our project. This is going to be used by the NW Cloud Maven plugin to obtain necessary information about the credentials and deployment properties.

nwcloud.properties

sdk.dir=$YOUR_NW_CLOUD_SDK_DIR
host=https://nwtrial.ondemand.com
account=ACCOUNT_NAME
application=APPLICATION_NAME
user=YOUR_USERNAME
synchronous=true

Lastly, let's create our WAR file by executing:

grails war

and now we're ready to deploy to NetWeaver Cloud by simply executing:

mvn nwcloud:deploy

You'll be asked to enter your password and after that you can now go to http://accounts.nwtrial.ondemand.com and start your application.

Improvements

Currently, I don't think the codes in the templates provided are efficient and I wrote them only for demo purposes. One can improve the ui5 codes and probably restructure it so that it follows the MVC design. Also, not all data types are supported right now so that definitely needs some time to work on.

Conclusion

It was really fun to play with UI5 and the NetWeaver Cloud SDK. I can't wait to try and explore Grails Scaffolding that uses UI5 for mobile.

14 Comments