Resource Handlers
Resource handlers are common platform layer abstractions used throughout Ignition to make common resource interaction tasks more consistent and reliable.
Resource handlers are automatically aware of the following aspects and save you time from having to implement them yourself:
- The (de)serialization logic provided by
ResourceTypeMeta - Redundancy aware configuration switching
- Resource renaming
SingletonResourceHandler
SingletonResourceHandler is used to manage the zero-or-one instances of a particular singleton resource that may exist on a particular Gateway. Instances of this class are automatically aware of when a given singleton resource does not exist within in a resource collection. A resource that does not exist within a collection is implicitly set to the default state defined in their ResourceTypeMeta.
Using SingletonResourceHandler
For simple use cases, you can create an instance of the SingletonResourceHandler using the builder attached to the class, and provide a lambda to run onChange that will receive the new configuration state:
var mySingletonHandler = SingletonResourceHandler.newBuilder(MyConfigResource.TYPE_META)
.context(context)
.onChange(
newConfig -> {
// unpack the new config object and apply it to something
})
.build();
If you need to customize behavior more, you can instead subclass SingletonResourceHandler. For example, overwritting the onResourceUpdated method will provide access to the old configuration resource:
var myResourceHandler = new SingletonResourceHandler<>(context, MyConfigResource.TYPE_META) {
@Override
protected void onResourceUpdated(
@Nullable MyConfigResource oldResource, @Nullable MyConfigResource newResource) {
// Note that this signature gives us access to the old config as well
super.onResourceUpdated(oldResource, newResource);
}
};
NamedResourceHandler
Much like the respective singleton handler, the NamedResourceHandler makes customizing named resources easier. For simpler uses cases, you can create an instance of NamedResourceHandler using the fluent builder pattern:
var myNamedHandler = NamedResourceHandler.newBuilder(MyConfigResource.TYPE_META)
.context(context)
.filter(resource -> true) // optional filter to customize what config is loaded by the handler
.onInitialResources(resources -> {}) // lambda invoked once on startup
.onResourcesUpdated(resources -> {}) // lambda invoked once per config change
.onResourceAdded(resource -> {}) // invoked per new resource
.onResourceUpdated(resource -> {}) // invoked per updated resource
.onResourceRemoved(resource -> {}) // invoked per deleted resource
.build();
Subclassing can be done and will be required for NamedResourceHandler if you want to process renames as discrete actions. By default, renaming a configuration resource reaches your handler as a deletion and a creation of a totally new resource. If you need to carry over data/state from the old instance to the new, override the isRenameAware method as shown below:
var myNamedHandler = new NamedResourceHandler<>(context, McpServerConfig.TYPE_META) {
@Override
protected boolean isRenameAware() {
return true;
}
@Override
protected void onResourceUpdated(ModifiedResource<McpServerConfig> modifiedResource) {
var oldResource = modifiedResource.oldResource();
var newResource = modifiedResource.newResource();
var isRename = modifiedResource.isRename();
}
};
The Javadoc for isRenameAware notes that all resources are required to have a valid UUID key in their attribute set, or else they will be ignored. All first party resource creation mechanisms will automatically include this UUID key, but it may cause unexpected behavior if resources are populated by third party tooling.