Skip to content

Resource Abstraction

Erik C. Thauvin edited this page Aug 25, 2023 · 9 revisions

RIFE2 has abstracted the way resources are handled. Traditionally, resources are looked up through the classpath in Java, it can however be very useful to be able to freely change where resources are coming from.

The ResourceFinder and ResourceWriter interfaces handle searching, retrieving and writing resources. RIFE2 provides a number of classes that have already implemented these interfaces: DatabaseResources, MemoryResources, ResourceFinderDirectories and even a ResourceFinderGroup that can combine a collection of different resource finders into one group.

Resource finders for flexibility

If you are looking up resources in your own application, consider using a ResourceFinder instead of the standard JDK's getClass().getResource(name), this will provide you with a lot of flexibility down the line.

Here are some of the methods that are available through a ResourceFinder instance:

getResource(String name);                      // retrieves resource for name
getContent(URL resource);                      // retrieves complete content of resource
getModificationTime(URL resource);             // retrieves modification time of resource
useStream(URL resource, InputStreamUser user); // read the resource contents as a stream

Resource writers

The resources from the classpath are effectively static. Unless you write a custom classloader that you slot into the classloader hierarchical, you can't really change which resources are available.

RIFE2 provides a ResourceWriter interface that complements the ResourceFinder interface. The DatabaseResources and MemoryResources classes implement both of these interfaces and thus allow the resources to be changes at runtime.

Here are some of the methods that are available through a ResourceWriter:

addResource(String name, String content);    // add a resource with name and content 
updateResource(String name, String content); // update a resource's content
removeResource(String name);                 // remove a resource

Pretty straightforward!

Template resources

The template engine relies on a ResourceFinder to get the content of any template that you want to instantiate. By simply changing the resource finder that is used by a template factory, you can store and retrieve templates from a database, pull them from memory, or even generate or transform them on the fly by implementing your own resource finder.

Let's set up the HTML template factory to use a MemoryResources instance:

public class HelloResources extends Site {
    // ...
    public void setup() {
        var resources = new MemoryResources();

        TemplateFactory.HTML.setResourceFinder(new ResourceFinderGroup()
            .add(ResourceFinderClasspath.instance())
            .add(resources));
    }
    // ...
}

You can see that we also used a ResourceFinderGroup that still uses the classpath to search for resources and if none can be found there, they will be retrieved from the MemoryResources instance.

We can add a few templates as resources, for example:

public class HelloResources extends Site {
    // ...
    public void setup() {
        // ... MemoryResources and TemplateFactory snippet from above
        
        resources.addResource("hello.html", """
            Hello <em><!--v name/--></em><br>
            <a href="{{v route:bye/}}">Bye</a>""");
        
        resources.addResource("bye.html", """
            Bye <em><!--v name/--></em><br>
            <a href="{{v route:hello/}}">Hello</a>""");
    }
    // ...
}

If we now use these templates in our web engine elements, they will be pulled from memory as expected:

public class HelloResources extends Site {
    Route hello = get("/hello", c -> {
        var t = c.template("hello");
        t.setValue("name", "John");
        c.print(t);
    });
    Route bye = get("/bye", c -> {
        var t = c.template("bye");
        t.setValue("name", "Jim");
        c.print(t);
    });
    // ...
}

You can find this complete example in the RIFE2 repository.

The example above changes the TemplateFactory.HTML instance that is used throughout the entire application. You can however also create your own TemplateFactory that is set up with a different ResourceFinder without affecting anything else, if that is more appropriate for your use-case.


Next learn more about What is bld?