Resources

Web applications expose resource URLs and handle requests for these resources. Path parameters complicate things but enable clean URLs. A mapping between a resource and application code which processes requests for the resource is called routing. Civilian has a low-config, intrinsic approach to routing.

Introduction

From a clients perspective a web application consists of resources which are addressed by a URL and can be requested via HTTP.
Internally, depending on whether the response content is generated at runtime, we talk about dynamic or static resources. Usually we differentiate static from dynamic resources by using the name assets for the static kind.

Then main tasks with regard to resource processing are

  • define the URLs of resources which can be requested by a client.
  • for every dynamic resource write code to process requests for that resource and dynamically generate responses.
  • provide files for all static resources (assets) and expose them via Civilians asset handling.
This chapter explains handling of dynamic resources. From now on we will simply call them resources.

Civilian uses the Resource class to model a resource. At runtime every application possesses a list of all its resources, organized as resource tree. The code which processes resource requests is implemented in Controller classes. In Civilian every resource is associated with (at most) one controller class.

Resource Controller
Resource("/users") com.myapp.UsersController
Resource("/messages") com.myapp.MessagesController
The next chapter will explain the inner workings of controllers. Here we describe how the resource tree is defined and how resources are mapped to controller classes. In webapp lingo this is called routing.

Some web frameworks require to explicitly specify the resource paths and map them to controller classes/functions/code. This is fine for a small set of resources but tedious for complex applications.
Civilian uses a more implicit approach: It scans the controller classes of an application and builds the resource tree using naming conventions and annotations within the controllers.

Before we explain how this works we complicate things a little bit and introduce path parameters.

Path parameters

From the applications perspective, it is tempting to interpret an URL as function, a request as function call, and request parameters as function arguments. For example in the following URL the id parameter could contain a database id for a customer entity, and the sample request would therefore return a HTML page showing data of customer 1345:
https://example.org/crm/customers/show.html?id=1345
Contrary to that view the concept of semantic or clean URLs demands a different style of URL design: URLs identify resources and should not contain technical parameters which are instead expressed in a RESTful way:
  • Customer 1345 receives an own URL (which is different from customer 1346), therefore the parameter id is made part of the request path.
  • The URL segment /show is dropped. Instead the HTTP method "GET" is used to tell the server that we intend to retrieve readonly information.
  • The path extension .html is dropped. Instead the HTTP Accept header is used to request a specific content-type.
GET /crm/customers/1345
Accept: text/html

Accordingly, updating customer 1346, by sending JSON data, might be done with

PUT /crm/customers/1346
Content-Type: application/json
From a functional point of view, path segments like /1345 or /1346 can transport the same information as parameters and can be used for the same purpose (namely identifying application data). We call such segments path parameters. (Please note that path parameters are not a syntax element of URLs but just an interpretation of path segments as parameter values).

Of course, in order to answer such requests, the application will examine path parameter segments, convert them into a value (e.g. a customer id) and use them in their calls to the data back-end.

Path parameters enable clean URLs, but internally require more sophisticated mapping of URLs to controller code: With path parameters applications can potentially expose an unlimited number of resource URLs.
Therefore routing can't just use constant resource paths, but instead needs to map path schemes to controller code:

/crm/customers/{:customerId [0-9]+} ⇒ com.crm.customers.id.IndexController
At runtime the path parameter segments are converted into a path parameter value:
/crm/customers/1345 run IndexController with customerId = 1345
In Civilian path parameters are not constrained to a single path segment, but can also span multiple path segments. Types of path parameter values can be of any kind, not just Strings or simple types.
/archive/2014/11/20 ≡ Date(2014,11,20)
/blog/posts/c3po/latest-musings ≡ Post(id:"c3po", slug:"latest-musings")
/en/intro ≡ Locale.ENGLISH
Extracting path parameter values from path strings is only one half of the game. When we want to build resource URLs (for instance to be used in HTML hyperlinks) path parameter values need to be formatted and inserted into a path scheme.


Civilian takes a very formalized approach in handling path parameters:

  • Every path parameter is described by an own PathParam object.
  • A PathParam object knows how to extract its value from one or more path segments and is able to format a value into one or more path segments.
  • PathParam implementations exist for most common use cases, but you can easily build implementations for more specific cases.
  • Defining the URL of a resource includes to describe which path parameters are used in what URL parts.
  • When a request is dispatched, and the matching resource is determined, all path parameters are automatically recognized, their values extracted and made available via Request.getPathParam(PathParam).
    This method does not take some string to identify the path param, but a PathParam object, therefore enforcing compile time safety.
  • When generating URLs via the Url class, you simply provide a value for every contained path parameter and let the Url class do the formatting.
  • All the path parameters used by an application are defined as application wide constants. Application implementations register their path parameters via a PathParamMap passed to the constructor of the application base class.
    For an example please take a look at the source code of the CRM sample:

Routing

The resource URLs of an application build a hierarchy or tree. So does a file system, and publishing a directory of static resources as website is easy:
File maps to URL Path
/webapp/index.html /index.html
/webapp/css/stlyes.css /css/styles.css

Base mapping from controller classes to resource paths

Java classes also build a hierarchy and we use this to derive a default mapping from package and class name:
Class maps to URL Path
com.demo.web.LoginController /login
com.demo.web.posts.ListController /posts/list
At application startup Civilian scans the classpath for all controller classes of the application. To be recognized as controller, a class
  1. must have a simple name which ends with Controller
  2. must (directly or indirectly) be derived from Controller
  3. must be in or below the controller root package which by default equals the package of the application class.
The qualified controller class - minus the root package, dots converted to slashes, uppercase converted to lowercase gives the resource path of the controller (relative to the application path).

One great advantage of this implicit mapping is that you can easily find the controller class given a resource url based on the naming conventions. And contrary to explicit routing it can't get out of sync when you rename classes or packages.

IndexControllers

Given the mapping based on package and class names we could not map a controller to the application root /. To enable this, controller classes named IndexController are treated specially, and are mapped to the path corresponding to its package:
com.demo.web.IndexController /
com.demo.web.posts.IndexController /posts

Using the @Path annotation on packages and class names

When you specify the @Path annotation on a controller class or package (in its package-info.java), the annotation value then is used to extend the path, instead of the simple class name or the last package part:

@Path("Civilian") package com.demo.web.civ /Civilian
@Path("temp") public class com.demo.web.civ.TmpController /Civilian/temp
@Path("index") public class com.demo.web.civ.IndexController /Civilian/index
Therefore use the @Path annotation to override the naming conventions when necessary.
(Or as an advanced technique alter the naming conventions).

Please not that the @Path value is not interpreted as absolute path (like in JAX-RS) but rather extends the path defined by the parent package.

Using the @PathParam annotation on packages and class names

Right now Controllers can only be mapped to constant paths. To map to path schemes which contain path parameters, use the @PathParam annotation on packages or controller classes.
The value of the annotation is the PathParam name.

@PathParam("userId") package com.demo.web.users.id</td> /users/{userId:[a-z0-9]+}
com.demo.web.users.id.ProfileController /users/{userId:[a-z0-9]+}/profile

Using the @Path annotation on controller methods

Given all these features every dynamic resource exposed by the application still requires an own controller class. Nothing wrong with that, but for very small controller implementations this might seem a liitle bit oversized.
As we will see in the Controller chapter, controller classes define special methods as entry points for request processing.
You can place @Path on such methods and create a virtual sub-controller class which processes requests for the corresponding sub-resource:
com.demo.web.SearchController /search
@Path("filter") com.demo.web.SearchController#processFilter() /search/filter

So using @Path annotations on methods you can implements multiple resource URLs in a single controller class.

URLs and extensions

In all the examples we followed the clean URL paradigma and did not use any extension in URLs.
Actually when a resource request is dispatched to a controller, the extension of the URL is simply ignored. By default both these requests URLs yield the same result:
/users
/users.html
But when dealing with clients which cannot properly set an HTTP Accept-Header to express their preferred response content-type, the URL extension can be used as a replacement for the Accept header.
GET /users.html ~ GET /users, Accept: text/html
Please study the ExtensionMapping class how to configure such behavior.

Fine points of resource mappings

No resource may be mapped to more than one controller. Doing so (e.g. using @Path annotations), results in an error.

Abstract controller classes will not be mapped to a resource.

Checking resource mappings

The admin app lists all resources of an application and the associated controller classes. This allows for a fast check if the reality matches the intended routing.