Linking
Civilian provides techniques to build correct URL strings and to prevent broken links.
Introduction
Web applications sometimes need to create URLs to reference application resources: Hyperlinks
in HTML responses, redirects, HATEOS locations etc. To represent URLs as strings –
as it is usually done – and manually build URLs is tedious and error prone.
Just think of formatting of parameter values.
Hyperlinks on the web can break, but at least they work good enough to be useful.
Being realistic about the web does not mean that we tolerate broken links within
our applications. But using URL strings again makes broken links more likely:
A change to the URL structure of an application requires to manually look for affected places in the code.
Civilians Path and Url classes help to avoid these pitfalls:
The Path class
The
Path class provides a normed representation of path strings:
- The root path is represented as empty string
""
- Any other path always starts with a
'/'
but never ends with a '/'
character.
The Path class supports common operations like adding two paths.
Many core objects like
Server,
Application,
Request,
Resource etc. are associated with an absolute path from the server root and
a relative path with regard to some parent context. They implement the interface
PathProvider to give access to these paths.
The Url class
The
Url class is a builder class to construct URL strings and
guarantees a syntactical correct form of the URL.
To create a Url object you need to call a Url constructor, passing the request object (or a RequestProvider like
a Controller) and an additional argument which describe the URL path:
- or a simple URL or path string, in case you want to address resources outside of the application (or address interal resources
in an unchecked way).
- a Path to build an Url for that path
- a PathProvider like the Application or the Request
to build an Url for the path of that provider.
- a Controller class to build a URL to the resource which is processed by that Controller.
- a Resource to build a URL to that resource.
RequestProvider rp = ...
Url url;
url = new Url(rp, "https://example.org/some/path");
url = new Url(rp, getApplication());
url = new Url(rp, SearchController.class);
Once constructed a Url object can be tweaked in many ways:
- Add or change query parameters in a type-safe way
- Add additional path segments or a fragment identifier
Url url = ...
url.addQueryParam("page", 5);
url.setFragment("details");
To retrieve the URL string simply call
Url.toString(). Behind the scenes this method does some automatic magic:
- Percent encoding of parameters is automatically applied.
- It inserts a session id into the URL if the client does not accept session cookies.
(You can also turn of this automation).
If the Url is constructed from a Controller class or Resource object (see next chapter),
it is easy to detect broken links. For example, delete a Controller class, and the compiler will tell you where is was
used to build an Url.
When constructed in such a way, the Url also knows about path parameters in the URL string and allows you to fill in values for them.
Url url = new Url(rp, MasterDataController.class);
url.setPathParam(CrmPathParams.CUSTOMERID, 123);
Use resource constants to create Urls
At runtime Civilian
knows all the dynamic resources exposed by an application,
the
path parameters which are
part of the path schemes of the resources, and which
controller processes requests for a resource.
This knowledge allows Civilian to associate a resource with a request and dispatch the request to its controller.
Normally this resource tree is built at application startup, by scanning
the classpath for the applications controller classes.
But Civilian also allows you to generate a special Java class from the resource tree, which defines
a Java constant of type Resource for every dynamic resource in the application.
An Url to a resource can now also be built by using that constant, as alternative to passing
the corresponding controller class.
Again, change your resource tree, then regenerate the constants interface, and the compiler
will tell you all places in your code which need adjustments.
Broken links, no more.
Example use of Resource constants
In the CRM sample we generated interface org.civilian.samples.crm.web.CrmResources
which defines constants for all resources of the CRM application.
To build a Url to resource /customers/{customerId}/masterdata
,
we write
Url url = new Url(CrmResources.root.customers.$customerId.masterdata);
...
String s = url.toString(); // at the end convert to a string to be used in the template
The Resource constants generator
org.civilian.tool.resource.ServerConstGenerator is the tool to generate the constants interface.
It prints a detailed help message when run without any arguments.
ServerConstGenerator expects the name of your application class and optional parameters:
java org.civilian.tool.resource.ServerConstGenerator param* app-class
One of the
-out:*
parameters must be used to the determine the location of the generated file:
-out:package <directory>
writes the output file into the package directory below the given package root directory
... -out:package src/main/generated ...
-out:dir <directory>
writes the output file into the given directory.
-out:file <file>
writes the output file to the given file.
The
-name
parameter sets the name of the generated interface. By default a name is derived from
the name of the application class. If the name is a simple name, it is qualified package of the application class.
... -name MyResources ...
The
-enc
parameter sets the encoding of the generated file. By default the encoding is UTF-8.
The
-v
parameter turns on verbose messages, the
-ts
parameter causes a timestamp
to be written into the generated file.
Avoid the classpath scan at application start
When Civilian starts an application, it scans the classpath to find the applications
Controller classes
and then constructs the resource tree. The resource constants interface does contain the exact same information
and can be used to avoid the scan. Just register during
app initialization:
public class MyApp extends Application {
...
protected void init(AppConfig config) {
...
config.setResourceRoot(MyResources.root);
}
}
But remember to regenerate the resource constants interface when you change the resource tree.
Another idea might be to only explicitly set the resource root in production mode.
Client-side linking
The techniques described so far are available on the server.
But suppose we write a client program to access server resources. Again we don't want to build URLs out of
error prone strings.
Since the
Resource and
Url classes are bound to a server-side execution context we cannot
use them directly.
Instead Civilian provides the
WebResource and
WebUrl classes for use in a client
program.
To obtain a Java interface defining WebResource constants exposed by the application, we use the command-line tool
ClientConstGenerator.
java org.civilian.tool.resource.ClientConstGenerator param* app-class
Its command-line parameters are similar to that of the server generator. Run the generator without parameters
to obtain a detailed help message.
The client sample demonstrates how to use WebUrl and WebResource.
Right now only a constants interface for a Java client can be generated.
We plan to add a similar output for JavaScript in a future release.