Deploying Web Apps

by Robert Pastel, Mark Woodford and Travis Forester for Grail 2. Revised for Grails 3 by Robert Pastel.

Deploying a web app is fundamentally the operation of packaging the files in your development environment and uploading the package to the server, but there are details of the application that you will need to attend to so that your application runs properly on the server. In particular you will need to:

  • Configure the database for operation on the server
  • Configure mail service to run on the server
  • Configure any file paths that might differ between development and production
  • Configure Build for the Tomcat
  • Package your application into a WAR
  • Upload the WAR to a server
  • Restart the tomcat
  • Check that it is running properly

Terminology

Environments

I need to introduce some terminology before we can configure and deploy of your applications. Grails can be configured to run differently in different “environments.” See

https://grails.org/wiki/environments

http://grails.github.io/grails-doc/latest/guide/conf.html#environments

Grails has basically three different environments:

  • development
  • test
  • production

Typically the development environment is the environment while you are programming on your computer at home. The production environment is typically a server that is accessed by the public. The test environment could be on a server that is setup to test integration of many services and web apps. Although, a development process should proceed from the development environment to the test environment and then finally made public in the production environment, we will shorten the process and use only the development and production environment. This implies that you will do final testing in the production environment.

WAR File

The second term to discuss is the “WAR.” WAR stands for “Web application ARchive.” It is basically a JAR file that has a specific directory structure for web applications.

http://en.wikipedia.org/wiki/WAR_(file_format)

In Java Server Page (JSP) technology, deployment proceeds by making a WAR file of your application and uploading the WAR file to a special directory on the server. The server un-packages the WAR, called exploding, knows how to interpret the files in your applications, and consequently can serve the files to the public. There are several JSP servers, see the list at Wikipedia

http://en.wikipedia.org/wiki/JavaServer_Pages

We will be using the Apache Tomcat server

http://en.wikipedia.org/wiki/Apache_Tomcat

Realize that Tomcat is in essences just a Java application. There is no special hardware associated with the server, but for the server to be public it needs be connected to the Internet and registered in a Domain Name Server (DNS).

Tomcat Server Directories

Domain

The domain name for the machine with the tomcat instances is

hci-dev.cs.mtu.edu

Catalina Base Directories

Catalina is the core component of Tomcat. Catalina is Tomcat’s servlet container. Catalina implements Sun Microsystems’ specifications for servlet and JavaServer Pages (JSP)

https://www.mulesoft.com/tcat/tomcat-catalina

http://stackoverflow.com/questions/32985051/what-are-the-tomcat-component-what-is-catalina-and-coyote

The Catalina base directories for the teams are located at

  • var/lib/tomcats/2017_hci_1/
  • var/lib/tomcats/2017_hci_2/
  • var/lib/tomcats/2017_hci_3/

Note that the tomcats/ has a “s”. The directory names for the different teams specify the year, the class and the team number. The team number is your team/group number. The format of the Catalina base directory is

<year>_<class>_<team number>

Each team has access only to their tomcat instance’s Catalina base directories. If your explore your tomcat directories, you will find

  • bin/ – this is a symbolic link
  • conf/ – contains the configuration files for your tomcat instance
  • lib/ – this is a symbolic
  • logs/ – this contains the log for your tomcat instance
  • temp
  • webapp/ – this is the appBase directory and contains the apps directories.
  •  work

We will talk more about these directories later, in particular the webapp/ and logs/ directories.

Configure the Database for operation on the server

When developing a Grails application, there are a number of database options available. You may choose to use H2 (default), HSQLDB, MySQL, etc. In addition, you can choose how the database will be stored:

  • hosted on a server: a service such as mysqld, running separately from the web application and possibly on a different machine, handles requests from the application to create, read, update, or delete data within the database
  • embedded: the database is located within the application itself, and all operations to the database are done directly by the application

For rapid prototyping, and for use cases in which the database accessed relatively infrequent  (i.e. there are only a few connections at once) an embedded database will work just fine. For small projects, embedded databases have the advantage of being easy to initialize and maintain.

Another design choice is whether to have the database stored on disk or in memory. If the database is expected to grow very large, then it would be better to store the database files on disk to prevent out-of-memory errors. Keeping the database in memory allows faster access times, but if something goes wrong, all data in memory will be lost. So, it makes sense that during development you would like to use a database that is stored in memory, so that you can quickly make and use the database. But during production you would like the database to be stored on the disk so that the data will persist between stopping and starting the web application.

I now explain how to configure your Grails application to connect to an embedded H2 database stored on disk.

Open the application.yml configuration file.

Direct your attention to the dataSource settings at the end of the file. The property driverClassName should be “org.h2.Driver”. Also, note that the username and password for the database are defined here.

Now locate the environment-specific settings. You should see code structure as below

environments:
    development:
        dataSource:
            …

    test:
        dataSource:
            …

    production:
        dataSource:
           dbCreate: none
           url: ... 
           properties:
                …

Grails allows you to configure specific settings for development, testing, and production environments. I suggest leaving the development and testing settings how they are. You need to make two changes to production settings.  In the production settings, just above the url, you need to change the “dbCreate” setting to “create”.

dbCreate: create

The “create” setting will make the database new each time you deploy a new war. If later you wish to save the database then change “create” to “none”. See Grails Gorm documentation

http://docs.grails.org/latest/guide/conf.html#dataSource

In the production settings, you need to change url, to specify the path to the database.

url: jdbc:h2:file:/var/lib/tomcats/<year>_<class>_<team number>/db/<app name>;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE

For example, if you are team 1 in the HCI class in the year 2017 make the app named “traveler” then the settings should be

url: jdbc:h2:file:/var/lib/tomcats/2017_hci_1/db/traveler;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE

Delete (or better comment out) all the “properties” options. These are for a database server not for H2 file based database.

Save the changes to application.yml. Now your Grails application is configured to connect to an on-disk H2 database at the file path specified for your the tomcat server instance. Now when you deploy your application (as a WAR file) to the tomcat server, a new H2 database with the given name will be created (if it doesn’t already exist).

For more details about how to configure the data source see the grails documentation

http://grails.github.io/grails-doc/latest/guide/conf.html#dataSource

and for a summary on how data source is configured by environments

http://grails.github.io/grails-doc/latest/guide/conf.html#dataSourcesAndEnvironments

Configure dbconsole for use in production

The dbconsole is not enabled by default in the production environment. In grails-app/conf/application.groovy file add at the bottom:

environments {
   production {
      grails.dbconsole.enabled = true
      grails.dbconsole.urlRoot = '/dbconsole'
   }
}

The setting “grails.dbconsole.enabled = true” enables dbconsole. The setting “grails.dbconsole.urlRoot = ‘/dbconsole’ demands the URL for the dbconsole relative to the web app use. So if you have port number 8081 and app name “traveler” then the URL for dbconsole will be

https://hci-dev.cs.mtu.edu:8081/traveler/dbconsole

 

You should make sure that access to dbconsole is secured by using spring-security core grails plugin. Also you will need give access to the admin role by adding the static rule in application.groovy

grails.plugin.springsecurity.controllerAnnotations.staticRules = [
...
   [pattern: '/**/dbconsole/**',   access: ['ROLE_ADMIN']]
]

When using dbconsole in production, make sure that dbconsole login fields are correct.

  • Driver Class: org.h2.Driver
  • JDBC URL: jdbc:h2:file:/var/lib/tomcats/<tomcat instance name>/db/<your app name> – This is the same URL that you used to specified for the database file location.
  • User Name: what you specified for the database username. Default is “sa”
  • Password: default is blank

Configure mail service to run on the server

If your web application uses a mail service then you will need to configure the mail for the production mail server. If your application requires users to register in order to use the app then your web app should be using the mail server to send a key to the user’s email address so that they can finish the registration. This is done for two reasons. The email address is a unique identifier for the user. Also, responding to the email address assures that the user is a “real” person and not a robot because a real user can click on the url specified in email. Finally the mail service is used to send a key to the user when they forget their passwords and need to reset their passwords. If you are using Spring Security UI plugin in your application then the above registration and forgotten password procedures are implement in your web app, and your web app is using a mail server and the Grails Mail plugin.

By default the Mail plugin is configured at localhost on port 25. The Mail plugin can be reconfigured in Config.groovy file. On the production server your application will use a special mtu email account. The email address is “mushroommail@mtu.edu”  with a password I’ll give you later. In the bottom of the application.groovy file add the following code:

// Spring Security Email Configurations
grails {
    	mail {
        		host = "smtp.gmail.com"
        		port = 465
        		username = "mushroommail@mtu.edu"
        		password = "actual-password"
        		props = ["mail.smtp.auth":"true",
            			"mail.smtp.socketFactory.port":"465",
            			"mail.smtp.socketFactory.class":"javax.net.ssl.SSLSocketFactory",
            			"mail.smtp.socketFactory.fallback":"false"]
     }
}

Note that you will have to replace “actual-password” with a password that I will give you via google docs.

You can read more about configuring and using the Mail plugin at

http://grails.org/plugins/mail

Configure any file paths that differ between development and production

Note these instructions are for Grails 2, and I have not verified that they work for Grails 3. If any team needs to configure file paths that differ between development and production environments then please let me know.

I suspect that most of the teams can skip this step. Meaning your production application is not using different file paths from your development paths. This is because you are storing uploaded files as blobs in the database. But sometimes databases are setup to store only the file path to the uploaded file rather than the complete file. If your database is configured like this then the file paths will be different on the development machine then on the production server. Below is technique to make it convenient to have different file paths.

This is an example of the solution used to determine the photo upload location in the Mushroom Mapper app.

A json file in the project (specifically, MushroomMapper/web-app/text/photo_cfg.json) contains internal and external paths to use for the upload location, for both development and production environments:

{
   "production": {
   		     "server": "/itss/local/home/cittemp/public_html/photos/",
   	     	      "client": "/photos/"
 	 },
  	"development": {
        		"server": "web-app/images/photos/",
       		        "client": "/MushroomMapper/images/photos/"
 	  }
}

This file makes it easy to find and change the paths if necessary, without delving into the code.

A function called initPhotos parses this file, and sets the current server and client variables depending on the current environment context. This function can be a method of a controller.  In the case of mushroom mapper it is a method of the main controller, ObservationsController.groovy. The variables set by the function are servletContext.photos.server and servletContext.photos.client.

void initPhotos() {
    def txt = servletContext.getResourceAsStream('text/photo_cfg.json')
    def cfg = new JsonSlurper().parseText(txt.getText())
    if (Environment.current == Environment.PRODUCTION)  
         servletContext.photos = cfg.production
    else servletContext.photos = cfg.development
}

The ‘servletContext’ variable (which is the ‘application’ variable when used in a gsp) behaves as a map whose data persists across all requests and sessions, so this function only needs to be called once for the lifetime of the server. Therefore, the following line is at the start of each action/method of the controller that uses these variables:

if (servletContext.photos == null) initPhotos()

‘photos.server’ is the internal path to the photos directory, and should be used with Groovy File objects, etc. ‘photos.client’ is the external uri, and should be used in the html, javascript, etc.

Naturally, the user running the server should have +rwx permissions on the directory in question.

Correct the Build for the Production Environment

External Tomcat Container Specification

Grails 3 embeds the Tomcat server in WAR file. The embedded tomcat server might interfere with the external container if it is a different tomcat version.

https://docs.grails.org/latest/guide/gettingStarted.html#deployingAnApplication

https://docs.grails.org/latest/guide/deployment.html

In the build.gradle configuration file at the root of project, find the dependencies section.

dependencies {
    compile "org.springframework.boot:spring-boot-starter-logging"
    compile "org.springframework.boot:spring-boot-autoconfigure"
    compile "org.grails:grails-core"
    compile "org.springframework.boot:spring-boot-starter-actuator"
    compile "org.springframework.boot:spring-boot-starter-tomcat"
    ...
}

....

Just above the dependencies section add the line to specify the tomcat version.

ext['tomcat.version'] = '7.0.69'

Where ‘7.0.69’ is the current tomcat version on hci-dev.cs.mtu.edu. Later, you will learn how to check the version number on the server.

Next we need to specify that the tomcat should not be included in the WAR file to do this change “compile” specification for the spring-boot-starter-tomcat to “provided” in the dependencies section. The edited version should look like

ext['tomcat.version'] = '7.0.69'

dependencies {
    compile "org.springframework.boot:spring-boot-starter-logging"
    compile "org.springframework.boot:spring-boot-autoconfigure"
    compile "org.grails:grails-core"
    compile "org.springframework.boot:spring-boot-starter-actuator"
    provided "org.springframework.boot:spring-boot-starter-tomcat"
    ...
}

Naming the War file

By default Grails name the WAR file with a version number. When deploying to an external tomcat, we do not want the version number. The WAR file name must match the project name or the web app will not deploy properly. You can edit the WAR file name if you want without hurting the contents.  Once your have configured the WAR name properly, you’ll not have to do this again.

The WAR is named in build.gradle configuration file in your project root directory. In build.gradle, add the following line to the bottom of the file.

war.archiveName="${war.baseName}.war"

Package your application into a WAR

To make the WAR file you run a grails command. You’ll need to do this step each time you want to deploy a new version of your web application. On your development machine, your home machine, make the war file in production environment mode. Production environment is the default for the “war” command but not for the “run-war” command. The command executed in your project directory is simply

grails war

If you are using IntelliJ IDEA, you can make the WAR file by clicking “Build” in the menu and selecting “Make Grails Web Archive”.  I have noticed that the “Make Grails Web Archive” menu item does not always appear in the “Build” menu. You can build the WAR file by using “Grails” in the “Tools” menu. Select “Run Grails Command” and enter “war” in the “Command” text field.

The build and compression will take several minutes. You will find the WAR file in your project’s build/lib/ directory. I always delete the old WAR file before building it, so that I know for certain that it is a new WAR file. The first time you build a WAR file there will not be libs/ directory. It will be made during the first building.

Uploading the WAR to your Server and Insure it is running

Tomcat servers have a special directory called the appbase. The WAR files are uploaded to the appbase directory and then tomcat will expand/explode the WAR file in the directory. This deploys the web application. If there is already a WAR file with the same name then the old WAR file should be deleted before uploading the new WAR file. Tomcat will detect that the WAR file is deleted and delete the corresponding exploded web application. This is called undeploying. You must undeploy before redeploying.

Because Tomcat is just a Java application, the machine running Tomcat can have multiple instances of Tomcat. In order that the Tomcat servers do not interfere with each other while serving pages, the different instances of the Tomcat servers are configured to receive their request and send their responses through different ports, specified by port numbers. Recall that while you were programming and testing your individual assignments you would point your browser to the URL

http://localhost:8080/cs4760progassign/
  • http: – represents the protocol, in this case hypertext transfer protocol
  • localhost – is the domain name for the server, in this case it means the current machine
  • 8080 – is the port number

Each team in the class has their own instance of Tomcat to use for deployment. But each Tomcat will be listening and sending to a different port.

A Sepecial Note about Protocol

The tomcat servers on hci-dev.cs.mtu.edu are configured to be a secure server, SSL. This is the https protocol. When browsering to your web app on hci-dev.cs.mtu.edu, you need to specify https, not http. You can not just type the domain into the browser’s URL window, you must add “https://”.

Port Numbers

I will give you the port numbers for your tomcat instance in file called tomcat in your team/group directory. I will also give you passwords that you will need in this file.

The URL to your app will be

https://hci-dev.cs.mtu.edu:<port number>/<app name>

So if you have port number 8081 and app name “traveler” then the URL will be

https://hci-dev.cs.mtu.edu:8081/traveler

Special Note about the number of WAR files

JSP and especially Grails web applications use lots of memory. They can also have a memory leak if they are not redeployed properly. The tomcat instances for the class have enough memory for only one additional web applications besides the web application that comes standard with tomcat.

In your appbase you’ll notice that there are already some directories which are web applications. They are:

  • ROOT/ – web app for bare domain
  • docs/ – documents for how to use tomcat
  • examples/ – example web applications

Leave these web apps in your appBase. They do not consume much memory. You can access ROOT, docs, and examples through your browser. You should do so in order to check that your tomcat instances is running properly. To get to the ROOT app use the URL

https://hci-dev.cs.mtu.edu:<port number>/

This page tell you about tomcat. It also specifies the tomcat version number.

Remember: ONLY add one more web app to your appBase, otherwise your tomcat might run out of memory and crash.

Uploading and Deploying

Using a SFTP client (WinSCP is a very good choice for Windows) login into the Tomcat machine. (See domain name above.) I highly recommend that you use RSA keys so that the client can auto log in during network distributions. If you are logging into the server from a machine not on MTU network then you need to use a virtual network (VPN) with mtu. Point a browser to

vpn.mtu.edu

Log in and then select “Standalone VPN client”. This will download “BIGIPEdgeClient.exe”. Run this executable file in admin mode, and it will install the “BIG-IP Edge Client” to your apps. You run the client and click the “connect button.” A window will pop up asking for your username and password. After clicking the logon button, the VPN is created. You need to keep the app running while use your SFTP client to upload files to the server. If you do not have a VPN connection, you’ll get an “network error” and not be able to connect.

In the SFTP client, navigate to the appBase. (See above for the appBase directory.)

If you have previously deployed a WAR file for your web app that you are currently deploying then it should still exist in the appBase alongside the exploded web app. The exploded wep app is a directory with the same name as the WAR file. Delete the old WAR in the appBase before uploading the new WAR.

Wait. Wait until Tomcat deletes the old exploded web app. If you do not wait then deployment will fail, and Tomcat will never explode the new WAR file.

When the old web app directory disappears, you should stop tomcat server using a sudo command so that tomcat server does not prematurely explode a partially uploaded war file.

You have access to a few commands that are normally run by the root user. These commands are called sudo commands and are proceed by “sudo.”

There are four commands

  • start – to start tomcat
  • stop – to spot tomcat
  • restart – to stop and start
  • status – to check tomcat status

The format running the commands is

sudo systemctl <command> tomcat@<tomcat instance name>

On the development server the tomcat instance name is the same as the Catalina base. For the 2017 HCI team 1, to stop the tomcat, they would type.

sudo systemctl stop tomcat@2017_hci_1

You will then have to enter the password for your team account.

Sometimes IT admin forgets to give the account sudo permissions. If you suspect that this is a problem see the sub section “Problems with Stopping and Starting Tomcat” in “Something Goes Wrong” section below for how to check sudo permissions.

After stopping the tomcat server, use your SFTP client to upload the new WAR file.

After the war file is uploaded check it permissions using “ls -l”, the long list format. The war file needs to have user and group read and write premonitions. The other permissions should be read only.

ls -l
...
chmod 664 <war-file-name>.war

Wait. Sometimes Tomcat takes several minute, up to 10 minutes, to see the new WAR file and explode it.

After the war file is uploaded to the webapp folder, you should start the tomcat server by running the sudo comand

sudo systemctl stop tomcat@<tomcat instance name>

Wait, wait, and wait longer. Tomcat takes a long time to start and explode the war file, generally 5 minutes. I normally point my browser to the ROOT app and wait for the page to load.

Point your browser to the web app and inspect it. (See above for port number.)

Deployment Scripts

Several students have written deployment scripts:

  • redeploy by Bryce Williams
  • deploy.py – Ryan Doyle’s Deployment script for Linux machines on MTU network. Instructions are on the ReadMe.

I recommend deploying by hand several times before using scripts so that you know what is going on.

Something Goes Wrong

The above procedure can go wrong. Some typically difficulties are

  1. The URL is wrong. Make sure you are using https protocol and you have the correct app name
  2. Memory overflow locking or crashing the server

You should first check that tomcat is running either by pointing the browser to the ROOT app or running the status command.

Second you should try restarting your tomcat.

In most cases stopping and starting tomcat will solve your problems. Sometimes you may need to delete the offending file or directory, e.g. the lockfile in the db/ directory.

You can get an idea of your problems by inspecting the log files. The log files are located in the logs/ directory in the catalina base directory.  The log file with the errors printed are in the catalina.***.log. Where “***” is the date specified by year first then month and then day.  A new log file is created each day that has errors. You find most recent entry in the log file by scrolling to the bottom of the log file. Pay attention to “SEVERE” errors.  If there is a SEVERE error. Study the chain of “Caused by” to determine the cause.

Class Not Found Error

If you see error like

Caused by: java.lang.ClassNotFoundException: ACLIP.Role

The tradition is for the package name to be lower case. But the package name in the error message is upper case. For some reason this causes confusion after deployment, even though it works in development. The solution is refactor the package name to be all lower case.

Problems with Database?

First make sure that production dbCreate is set to “create” in the application.yml. Otherwise the database will not be made. Later you may want to deploy a new WAR without changing the database then make sure that BootStrap.groovy does not add to the database and change dbCreate  to “none” in the application.yml.

Check that the db/ directory is made. It should be made in your catalina base directory. Examine the directory for files. If your app is still having trouble accessing the database you could delete these files and restart tomcat.

If the db/ directory is not created. Try creating the db/ directory and redeploying.

Apps without Domain Objects

If your app does not have Domain Objects then it is not using a database. Even if the app is not using a database the production data source configuration in application.yml must be correct because the app will try to connect to the data source during start up. I solve the problem by copying the data source configuration for development into production.

Problems with Styling?

I have had my CSS styling broken on the production server when it worked fine on development. I have fixed this by changing the asset pipeline settings in “build.gradle” to not enabling CSS minifying. Search for the assets section in “build.gradle” and change it to

assets {
    minifyJs = true
    minifyCss = false
}

Problems with Stopping and Starting Tomcat

Sometimes IT admin forgets to grant sudo or all the necessary permissions to the user account. If you have problems with sudo commands, you can check what you can do with sudo by

sudo -l -U <username>
e.g. sudo -l -U 2017_hci_1

Good luck