Scalatra-Scalate 7. Deployment

Robert Crowther Apr 2022
Last Modified: Feb 2023

Prev Next

The Scalatra website has instructions about deployment. For Java‐world, these instructions are well‐explained and concise. However, I’m going to walk a through‐line through them. We’re going to build a deployment server that is intended to sit behind another server, so will not face the web, so needs no tricks or security considerations. The server Jetty, which is used for the development, is good for this. Also, to talk about Jetty adds to the Scalatra guides.

Config Scalatra for generating WAR files

Check /pathToYourApp/target/webapp/WEB‐INF/web.xml. It should read,

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1">

  <!--
    This listener loads a class in the default package called ScalatraBootstrap.
    That class should implement org.scalatra.LifeCycle.  Your app can be
    configured in Scala code there.
  -->
  <listener>
    <listener-class>org.scalatra.servlet.ScalatraListener</listener-class>
  </listener>
</web-app>

This is where Java Servlet apps are traditionally configured for serving. But the Scala developers have tapped the configuration to make a nicer API. So check ‘/pathToYourApp/src/main/scala/ScalatraBootstrap.scala’, it should read as,

import com.rcrowther.app._
import org.scalatra._
import javax.servlet.ServletContext

class ScalatraBootstrap extends LifeCycle {
  override def init(context: ServletContext) {
    context.mount(new ScalatraServlet, "/*")
  }
}

Add this line to kill the development returns that list debug info, like templates tried, errors thrown, etc. (any name not started with ’dev…’ will be ok). You can do this with an environment variable also, which is will work automatically on deployment. Here’s the direct way using configuration,

context.setInitParameter("org.scalatra.environment", "production")

And here’s what you would add to ScalaraBootsrap for the environment variable,

    val envName = sys.env.get("SCALATRA_ENVIRONMENT") match {
        case None => ""
        case Some(name) => name
    }

Set the SCALATRA_ENVIRONMENT variable to a value starting in ‘dev’ and the Scalatra instance is in development mode. Set it to anything else, or don’t set at all, and the Scalatra instance is in production mode.

You can influence some other things in the ScalatraBootstrap function, such as change ports, change headers, bootstrap database connections etc. Here I switch CORS off (either the server routes accept no user data, or the headers are handled by a proxy server), and jetty runs on localhost with a new port,

context.setInitParameter("org.scalatra.cors.enable", "false")
context.setInitParameter("org.scalatra.HostName", "myapp.local")
context.setInitParameter("org.scalatra.Port", "443")

There’s plenty more. We’re not bothered.

Choose a Servlet container

The Scalatra instructions talk about a few ways to do this. As the Scalatra writers say, “there are many Servlet containers available”. Which to choose? The problem is they are all different, and loaded with Java‐world jargon. Scalatra’s example is of Apache Tomcat. Tomcat is an old server. It is intended as a reference Servlet container, and comes with a few extras such as templating engines, file serving and temporary re‐routing (note: the Apache server itself can not run Servlets—Tomcat is sometimes used behind it). I can’t be bothered to wade through the promotional material to reach some facts. For this guide I use Jetty again. Jetty is a server for when you have no extra needs, and one thing on your mind.

You need a Java JVM on your machine. OpenJRE will work. Now you need Jetty. But you get to the site, which version to get? I’m going to tell you—2022 you need Jetty 10. Now you can skip the text under this heading. But if you wade into trouble, come back, see what I did.

A short story of picking a Jetty version

You could look in ‘/pathToMyApp/build.sbt’, see which Jetty version the development environment uses. That’s got some guarantees. Then again, these components are supposed to work to an interface standard, yes? For what it’s worth, my Scalatra used Jetty 9.4. But 10 and 11 are the supported Jetty versions, so I downloaded 11.0.

I made WAR deployment before Jetty threw an error I was not equipped to handle,

... java.lang.NoClassDefFoundError: javax/servlet/ServletContextListener

The error was an issue. Javax is a tag for “we, the Java maintainers, are clearing this code for release, but it’s new, untried, or not core provision”. But, I complain, Servelets are stock Java from Java culture. And, except in rare scenarios, Java culture is strong for backwards compatibility. ServletContextListener has been about for, I guess, near 12 years. I found a reference in the logback‐classic‐1.2.3.jar,which makes sense—the logger wants to know when contexts start and stop. This API is used directly in Scalatra for boot and shutdown callbacks. I go rooting in the JAR files in Jetty and there is no file of that name.

I have no idea where the error comes from. So I try tighten error margins. I get a Jetty 9 same as the development build uses—‐got to give hope, yes? And I get Jetty 10 too, because it has a curious README note about being current with Jetty 11 but with an API change (not for Servelets, surely?). Jetty 9 gives me grief about filepaths, but at this point I don’t care. Then it works. So it looks like Java Servlets, or Jetty’s handling of them, has changed. And then, Jetty 10 works. Alright. Jetty 10 it is. Now I can work on useful things.

Install Jetty

Download a Jetty. Put it somewhere (/opt/ is the usual, but whatever) and extract. Start a terminal,

cd /jettyTopDirectory/

Note something. Jetty documentation keeps talking about $JETTY_HOME. This is the place you are now, /jettyTopDirectory/, so I’m stripping all that. Jetty will issue warnings—a security risk, and messy for multiple configurations—but we build here a trail deploy, not the final version. You’re running the start.jar for most of this, ok? Go to the directory, work direct. Run,

java -jar start.jar

Should return errors. Because the Jetty you have is a minimal shell. Jetty has nothing to run.

Jetty Modules

Let’s look what’s available,

java -jar start.jar --list-modules=*

What this doesn’t tell us is what Scalatra’s rig contains. Now, I’ve already talked about how it is beyond mortality to ask for that information from SBT. I going to do everyone a favour, you and me, and tell you Scalatra configures this, and a whole load more, in ‘/home/rob/Downloads/scalatra‐main/project/Dependencies.scala’. Here it is,

We’re after a bare minimum, behind proxy server, so that’s the basics. But you also need, because this is not a development server, the ‘deploy’ and ‘http’ modules,

deploy

Enables web application deployment from the $JETTY_BASE/webapps/ directory

http

Basic outward facing HTTP delivery

This assumes web, not UNIX, sockets. Now we know what the development server uses, let’s put the bare bones into the deploy server (the rest for you to choose),

java -jar start.jar --add-module=jsp
java -jar start.jar --add-module=server
java -jar start.jar --add-module=servlet
java -jar start.jar --add-module=webapp
java -jar start.jar --add-module=plus
java -jar start.jar --add-module=deploy
java -jar start.jar --add-module=http
java -jar start.jar --add-module=websocket-jetty

You may not need JSP, but Scalatra is geared into JSP, and the build will spit warnings. And, ok, if you’re interested in a Unix Socket… note that Java 16+ will use a new module, unixdomain‐http. Also, you don’t need http module for a unixsocket. For now,

java -jar start.jar --add-module=jsp
java -jar start.jar --add-module=server
java -jar start.jar --add-module=servlet
java -jar start.jar --add-module=webapp
java -jar start.jar --add-module=plus
java -jar start.jar --add-module=deploy
java -jar start.jar --add-module=unixsocket

You may also want,

java -jar start.jar --add-module=gzip
java -jar start.jar --add-module=security
java -jar start.jar --add-module=logging-jetty

You’ll find, via dependency resolution, these may pull in other modules.

List modules,

java -jar start.jar --help

Can I remove a module? No, but you could clear out the initialisation trigger in ‘/start.d’. That will disable them. Also good,

java -jar start.jar --help

I assume you are already on a packed development machine, or maybe a test deploy. If you have Scalatra on the same machine, it will be using that overloaded port 8080, so you’ll need a new port. If you need that, in ‘/start.d/http.ini’ uncomment,

...
## The port the connector listens on.
# jetty.http.port=8200
...

Then change to something else. Now,

java -jar start.jar

if you go visit, you get a 404 error,

http://0.0.0.0:8200

But that 404 error is a Jetty response! Ask me that’s ace. No package management, with it’s undocumented configurations and placements, no computer language installations, no permissions issues (that’s my responsibility, later). And configuration in a few well‐placed files. And solid help straight from the commandline. Java as it could have been, should be.

Deploy the webapp

Ok, this is welcome! A running server. Time to see if this Servlet/WAR placement gear works. Back to the Scalatra development environment. Run SBT but don’t launch the server, run this command (if you must, ‘task’),

package

Mine didn’t tell me where the package went. It’s in ‘/pathToYpurApp/target/scala‐X.XX.appnane_versionString.war’.

Copy the WAR file to Jetty. Put it in the ‘webapps’ directory (if the directory not exists, you didn’t install the ‘deploy’ module). Now Jetty must be restarted. From Jetty documentation,

The auto discovery features of the Servlet Specification can make deployments slow and uncertain. Auto discovery of web application configuration can be useful during the development as it allows new features and frameworks to be enabled simply by dropping in a jar file. However for production deployment, the need to scan the contents of many jars can have a significant impact at startup time.

Applies to all Servelet containers. Jetty configures for this.

nano jetty_HOME/start.d/jetty.deploy'

Choose if and when to extract WARs.

End of Part Seven

End of Part 7. Ought to be the end of things. But it isn’t, because it never is, not here. In this world, it never is. And, if you’ve been following, I’d be selling and selling short if I left it here. Part Eight awaits.

Refs

Apache documentation on Jetty. You will need quiet, coffee, and endurance,

https://www.eclipse.org/jetty/documentation/jetty-11/operations-guide/index.html

Good instructions (in this world, miraculous) on server deployment,

https://zetcode.com/java/jetty/install/