Scalatra-Scalate 4. Static Resources

Robert Crowther Apr 2022

PrevNext

Scalatra is routing. Scalatra code has put Scala into our hands and, by way of SBT, we have access to Java codebases. We have templates working. If you’re looking to go the route of JSON, theres help on the Scalatra website. Scalatra installs JSON libraries (and, reputedly, some Swagger integration too). But I think about something else. If you are templating with some inttention of delivering webpages, then you’ll need CSS, images, JS, that kind of thing. And there’s not much help out there. And, unfortunately, this activity has no Scala groove to follow.

The issue of multi‐server deployment

Scalatra/Jetty is made to spring for specialist responses. If you use Scalatra for a specialist webpage respose, when deployed the code will sit in a server behind another server. As well as forwarding specific requests to the server/servelet, the frontend server(s) will know how to serve CSS, images, JS, all the other files a user’s browser may request. So, on deployment, there is no need for deployment of static resources.

However, this creates development blindness. The web output of a Scalatra/Jetty app, indeed any Sinatra clone, is raw. And you need to keep the URLs, because they will work on deployment. URLs like,

/static/css/megopolis.css

Which in development will find nothing. There are ways round this. For example: work blind. Or run behind a big server for development—heavy, but some frameworks demand that anyway. Or regard this position as an early deployment stage. Or set up a temp/test serve rig in Scalatra.

Setting a Scalatra to serve static resources

To serve files from inside Scalatra, you need to decide where those files will live. If your Scalatra is on the same devevelopment computer as your main site development, this is easy. Get the files from your Content Management System/Framework. Yes, permissions accepting, this is easy to do. Or you can copy the files over to the Jetty/Scalatra app, probably at,

projectName/src/main/webapp/static/css

etc. Duplication of files is not good development, but may work in some circumstances. I suppose you could use also a soft/hard link but, personally, I’m not fond of breaking encapsulation like that.

Now you’ve decided where the files will be, you need to link. Now, I’m sad to tell you, but you must dive into Java‐world. See, Scala has many tricks and slick compressions of Java‐madness, but reading files is not one of them. For years, Scala has provided a gadget,

import scala.io.Source
   ...
   Source.fromFile("/home/rob/Srv/adayEnv/aday/aday/static/css/myCSS.css).mkString
  }

The idea was to have a Ruby‐like interface for file reading. But ‘Source’ is not flexible and doesn’t close files. As for Java file‐reading, it’s awesome and aweful—after thirty years, it can’t read a text file whole. And there’s a second issue here. Scala has so far been defending with all sorts of tricks. When you return data from a ‘route’, Scalatra guesses at the form, tags it onto a response as it sees fit, then guesses a MIME‐type. This can all be overridden. When we are taking about delivering a variety of PNG, SVG, font files, JavaScript, and whatever else, we need to become assertive.

Well, I’m no expert in Java file‐reading. But Scalatra has thought of us. Apache Commons is pre‐loaded into Scalatra. You could use readFileToString, which also closes files. But I’m going to plunge for the Scala library OS‐Lib. It’s too clever for me, but looks, as you will see, neat. No import needed(!). Get it loaded into SBT. Here’s a route for CSS,

  get("/static/css/:name") {
    contentType = "text/css"
    val path = os.Path("/pathToMyStaticFolder/static/css/" + params("name"))
    os.read(path)
  }

Ok, this loads CSS, so Scalatra needs to see a String return—a complete string, not a list/iterator/array. Which ‘os.read’ provides. To be on the safe side, we don’t let Scalatra guess MIMEs, the MIME type is stated.

As for images, they need to be fed back to Scalatra code as a stream of bytes. Except, not all images. I have SVG images—they need to return as text,

  get("/static/images/:name") {
    val name = params("name")
    val path = os.Path("/pathToMyStaticFolder/static/images/" + name)
    if (name.takeRight(3) == "svg") {
      contentType = "image/svg+xml"
      os.read(path)
    }
    else {
      contentType = "image/png"
      os.read.bytes(path)
    }
  }

‘takeRight’ is a Scala special—return the last three characters. After code knows the type of file, it can set the MIME. The return is a String or ByteArray, dependant.

Now for fonts, which need again to be a ByteArray,

  get("/static/fonts/:name") {
    contentType = "font/woff2"
    val path = os.Path("/pathToMyStaticFolder/static/fonts/" + params("name"))
    os.read.bytes(path)
  }

Percussion point—if you connect cross‐filesystsem the static files are served from Jetty, not your whatever development server. That can throw issues with the files, URLs, and MIMEs themselves. But hey, if your entire codebase becomes more rigorous, this is good, yes? And how groovy is this code? Scraps of lines you wouldn’t think made sense (they’re surfacing from the underlying class churn), except you know they do. Check this works,

http://127.0.0.1:8080/static/css/megopolis.css

Note that, on deployment, the production server will reroute the static URLS. Probably best to remove the extra routes anyway, but test deployments will work fine.

End of Part Four

Ok, you should be heading in your intended direction now, or direction of interest. I could stop the guide here, or you could skip on to Part 7 Deployment. But I write this guide for people who have not encountered Java. In which case, I may as well be dumping you in ploughed field which could be anywhere between the American mid‐west, Argentina, Madagascar or Mongolia. I can’t do that. Part Five awaits.