While the JDrupes Builder provides a set of standard resource types, it is sometimes useful to define additional resources in build projects to model specific build artifacts or processes.
Defining a new resource type is straightforward if it extends an existing resource interface and does not need additional methods. For example, if you want to define a new type of file resource, you can extend the FileResource interface:
public interface AudioFile extends FileResource {
static AudioFile from(Path path) {
return ResourceFactory.create(new ResourceType<>() {}, path);
}
}
By extending FileResource, your new resource type inherits all its methods,
such as path() and cleanup().
As shown in the example above, new resource instances are typically created
using the
ResourceFactory.
The ResourceFactory.create method takes a
ResourceType and any
additional arguments required by the resource’s constructor.
The implementation of ResourceFactory uses a Java
Proxy
to provide the implementation of the interface. This means you don’t need
to write a concrete class that implements your new interface; the builder
handles it dynamically as long as the interface does not define additional
methods (static and default methods can be added).
Following the style of existing resource interfaces in the JDrupes Builder
API, it is recommended to provide a static from method in your interface.
This method serves as a convenient factory for creating instances of the
resource and encapsulates the call to ResourceFactory.create.
static AudioFile from(Path path) {
return ResourceFactory.create(new ResourceType<>() {}, path);
}
This makes the usage of your new resource type consistent with the rest of the API:
AudioFile audio = AudioFile.from(project.buildDirectory()
.resolve("background.mp3"));
If your new resource type needs to provide additional methods that are not
part of the base interface, the dynamic proxy mechanism is no longer
sufficient. In this case, you must provide a custom implementation of the
resource and a corresponding ResourceFactory to instantiate it.
First, define your interface with the additional methods:
public interface ExtendedResource extends Resource {
String extraInfo();
}
Next, create a class that implements your interface. It is recommended to
extend ResourceObject (from the core package) to inherit standard
resource behavior:
public class DefaultExtendedResource extends ResourceObject
implements ExtendedResource {
private final String info;
public DefaultExtendedResource(ResourceType<? extends ExtendedResource> type,
String info) {
super(type);
this.info = info;
}
@Override
public String extraInfo() {
return info;
}
}
Create a factory class that knows how to instantiate your resource. You can
use CoreResourceFactory.createNarrowed to handle the proxying if your
factory should also support the proxy mechanism to create specialized
versions of your new resource type:
public class MyResourceFactory implements ResourceFactory {
@Override
public <T extends Resource> Optional<T> newResource(ResourceType<T> type,
Project project, Object... args) {
return CoreResourceFactory.createNarrowed(type, ExtendedResource.class,
() -> new DefaultExtendedResource(
(ResourceType<? extends ExtendedResource>) type,
(String) args[0]));
}
}
Finally, register your factory as a service by creating a file named
META-INF/services/org.jdrupes.builder.api.ResourceFactory in your
resources directory, containing the fully qualified name of your factory class:
com.myproject.MyResourceFactory
You can find examples for ResourceFactory implementions in the jdbld
sub-projects for supporting
Java specific
resource types of for handling
Maven repository
related types.