Skip to content
/ rms Public

RMS is a simple Resource Management System for Java

License

Notifications You must be signed in to change notification settings

FoxSamu/rms

Repository files navigation

RMS (Resource Management System)

This is a very simple Java tool for games and other apps to manage resources.

Installation

The current version is 1.1. This version is compatible with Java 17 and above.

The artifact can be installed from my Maven repository.

Gradle

// Kotlin DSL:

repositories {
    // Add my repository
    maven { url = uri("https://maven.shadew.net/") }
}

dependencies {
    // Add the artifact
    implementation("dev.runefox:rms:1.1")
}
// Groovy DSL:

repositories {
    // Add my repository
    maven { url "https://maven.shadew.net/" }
}

dependencies {
    // Add the artifact
    implementation "dev.runefox:rms:1.1"
}

Maven

<repositories>
    <!-- Add my repository -->
    <repository>
        <id>Runefox Maven</id>
        <url>https://maven.shadew.net/</url>
    </repository>
</repositories>

<dependencies>
    <!-- Add the artifact -->
    <dependency>
        <groupId>dev.runefox</groupId>
        <artifactId>rms</artifactId>
        <version>1.1</version>
    </dependency>
</dependencies>

Usage

There are 3 core components to the system:

  1. ResourceManager
  2. Resource
  3. ResourceType

1. Creating the ResourceManager

You need at the very least a ResourceManager, which is a concrete class you can simply instantiate:

ResourceManager mgr = new ResourceManager(path, "namespace");

There are 2 main things you need to create the resource manager:

  • A Path, to the root directory that contains all your resources.
  • A "standard namespace", which is the main namespace of your application. This is usually one identifier, like rms.

There is a special static method on ResourceManager that allows you to get a Path for a resource folder on the class path. This method uses the ClassLoader.getResource method, which needs a resource file in order to work. So, to get the resources from your jar file, you need to define some empty file in the root directory of your resources folder, and call ResourceManager.classpath("/.resources_root") if it's named .resources_root. The returned path is then the parent directory of that file, possibly inside of a jar file.

ResourceManager mgr = new ResourceManager(ResourceManager.classpath(".resources_root"), "namespace");

Whenever you're done with your resources, or need to reload all of them, call ResourceManager.dispose.

2. Implementing Resource

The next step is to create a Resource implementation. Resource is an interface you can implement and it just has one method: dispose. This method is called by the ResourceManager upon ResourceManager.dispose. Usually, this method does nothing, but if your resource acquires some native resources (like off-heap memory) that must be cleaned up, then this is the place to do that. This is a resource that just contains a string:

public class ContentResource implements Resource {
    private final String content;
    
    public ContentResource(String content) {
        this.content = content;
    }
    
    public String content() {
        return content;
    }
    
    @Override
    public void dispose() {
        // Not applicable
    }
}

3. Loading the Resource using a ResourceType

Once you've got a Resource implementation, you have to define a way this resource is loaded. The ResourceManager assumes this resource to be in a certain folder, so all you have to do is locate it inside of that folder, read the files that must be read and turn them into your resource. Typically, the resource is in a file with the same name as the name of the resource. Resources may have path names, so you can group similar resources of the same type into a subfolder.

The loading of resources from the filesystem is done by a ResourceType. The ResourceType interface has two methods: load and createFallback. First load is called to locate and load the resource, and if that fails, createFallback should be able to create some kind of fallback in case the resource fails loading. For above example we could create the following resource type:

public class ContentResourceType implements ResourceType<ContentResource> {
    @Override
    public ContentResource load(ResourceManager res, String ns, String name, Path directory) throws Exception {
        // The `directory` parameter already accounts for the namespace, so you don't need to incorporate the
        // namespace into locating the resource.
        try (BufferedReader in = Files.newBufferedReader(directory.resolve(name + ".txt"), StandardCharsets.UTF_8)) {
            StringWriter writer = new StringWriter();
            in.transferTo(writer);
            return new ContentResource(writer.toString());
        }
    }
    
    @Override
    public ContentResource createFallback(ResourceManager res, String ns, String name) {
        // The fallback should be created from memory, it should not load from a file.
        return new ContentResource("[Failed loading.]");
    }
}

You may, however, encounter resources made of text in this specific structure quite often. There are 3 common interfaces that extend ResourceType which ease the loading of single-file resources (which are resources of just one file which has the name of the resource):

  • FileResourceType is a type for loading binary files: it provides you with an InputStream that streams the contents of the file.
  • TextResourceType is a type for loading text files: it provides you with a Reader<