Project Resources, as described in the Programming for the Gateway section, are the primary data persistence mechanism for modules that need to store data on a per-project basis. The definition of what a resource represents is completely undefined, allowing modules to define them in any way that they like. There are certain paradigms that generally should be followed, such as displaying the resources in the Project Resource Tree in the designer and adding actions for creating new instances to the Designer menus.
Project resources are essentially raw data with a unique id. There are a variety of useful accessories built in, such as support for folder hierarchies, but at their core they are very simple. They are contained in a Project object, which may represent either the full project, or a subset of it. For example, when resource changes are communicated to the modules, an instance of Project containing only the modified rows is delivered.

Creating New Resources

The process of creating a new resource involves generating an id for it, creating the resource, and then updating the project to include it.

Step 1 - Create a new resource id

Resource ids must be created atomically through the resource lock manager. For example:

long newId = designerContext.getLockManager().newResourceId();

Step 2 - Create new ProjectResource

Instantiate a new ProjectResource class with the new id, your module id, resource type id, and other data. For example:

ProjectResource resource = new ProjectResource(newId, moduleId, resourceType, resourceName, ApplicationScope.GATEWAY, resourceData);

 

Remember that project resources dictate a scope that indicates where they will be used. This is primarily to avoid sending resources that are only used in the gateway to the clients. The designer, of course, will have access to all resources. This example expects that you have all of the fields already defined in variables. The resource data is a byte array, and can be obtained by any method that you desire. Most commonly though, modules will simply use the serialization facilities provided by Igntion. For example, the following code could have easily proceeded the previous example:

MyModuleObject newObj = new MyModuleObject();
XMLSerializer s = designerContext.createSerializer();
byte[] resourceData;
s.addObject(object);
resourceData = s.serializeAndGZip();

 

Step 3 - Updating the Project

Finally, the resource is added to the project:

    designerContext.updateResource(resource);

Creating Folders

While folders are simply project resources like the others, the way that they are created is a bit different than other resource types. The procedure resembles the steps above, in that a new resource id must be generated, and the folder is defined with a scope and module id, but instead of instantiating a new ProjectResource and adding it to the project, you simply call DesignerContext.addFolder(). For example:

    designerContext.addFolder(newId, moduleId, ApplicationScope.GATEWAY, folderName, parent);


This function handles generating a new UUID for you, and also ensures that the folder name will be
unique for the module.

Modifying Resources

The general process of modifying resources is similar to that of creating them, in terms of serializing data, setting it, and calling updateResource(), with the exception that care should be given to managing Resource Lock s.

What Are Resource Locks

Since Ignition supports concurrent designer sessions, it is possible that two people may try to edit the same resources at once. Allowing both parties to edit a resource and then simply using one would not be intuitive to the user. To prevent this, resources can be locked for editing, which is communicated through the gateway so that all other designers are prevented from editing the same resource. Resource locks are tracked by session, and are automatically released when the session expires or the project saved. Still, careful management of locks by the module writer is important, as it reduces the number of times users encounter unexpected locked resource errors.

Obtaining, Updating and Returning Locks

All lock management is performed through the ResourceLockManager, which is obtained through the static ResourceLockManagerFactory.getManager() function.
The process for properly managing locks is as follows:

  1. Call ResourceLockManager.requestLock() for a resource id. This function returns a boolean indicating whether the lock could be obtained. If it returns false, you should not proceed to edit the resource.
  2. Modify the resource. Call ResourceLockManager.updateLock() for the resource, to notify the lock manager that the resource has, in fact, been modified. This will prevent other sessions from editing the resource until the project is saved.
  3. When done, call ResourceLockManager.releaseLock(). This is especially important if no changes have been made to the resource (and updateLock() hasn't been called) so that other designer sessions may edit the resource.


At any time, the ResourceManager.hasLock() function may be called to see if the current session currently has the lock for the resource. This is important because the updateLock() and releaseLock() functions require that the caller knows that it has the lock.

Example

This contrived example shows the general process of modifying a resource object. The actual process would involve multiple stages of locking the resource, presenting it for modification, and later committing it.

long resourceId = getCurrentResourceId(); //Would return the currently selected resource
if(ResourceLockManager.getManager().requestLock(resourceId)){ 
    try{
        ProjectResource resource = designerContext.getProject().getResource (resourceId);
		MyModuleObject object = getObjectFromResource(resource); //This function would deserialize the data in the resource
		
		... edit the resource object ... 
		
		resource.setData(serializeObject(object)); //User defined function serialize as described above
		ResourceLockManager.getManager().updateLock(resourceId);
	}finally{ 

	}
}else{
	//Make sure to release the lock, even if an error occurs 
	ResourceLockManager.getManager().releaseLock(resourceId);
	//Could not obtain lock, show an error
}

 

Deleting Resources

Deleting resources is as simple as calling DesignerContext.getProject().deleteResource() for the resource id with the markDeleted flag set to true. Note, however, that in regards to locking, the deletion of a resource should be treated the same as a modification, and the procedure outlined above should be followed.

Listening for Project Resource Changes

Listening for project resource modifications is as easy as implementing a ProjectChangeListener and registering it by calling ClientContext.addProjectChangeListener(). The ProjectChangeListener receives only the subset of the project that has changed, or the specific resource modified. At any time, however, it's possible to get the full project by calling Context.getProject().

Folders the Project Resource Hierarchy

As mentioned in the Creating New Resources section, ProjectResources support hierarchical organization with folders. Folders are simply ProjectResources themselves, with the important distinction that their resource data is actually a UUID (universally unique identifier). This unique id stays consistent across resource renaming, and is what is used by resources to identify their parent folder.
Each resource, therefore, has a "parent uuid" field that defines the folder resource it belongs to. A null value indicates that the resource is at the root level. Folders are stored according to the module that they belong to, but are all of the same resource type, defined by ProjectResource.


FOLDER_RESOURCE_TYPE

To help make working with folders easier, the Project class has a variety of functions for browsing the structure. Additionally, the ProjectResource class has the getDataAsUUID() function that makes it simple to obtain the UUID value of a folder resource.