Developing Cartridges (legacy)
Using a cartridge, you can extend the tribefire platform by implementing custom logic.
On this page
General
A Tribefire cartridge allows you to extend the platform. Using a cartridge you can create a new extension point implementation and then integrate it into tribefire via Control Center.
You can develop your custom implementations of regular tribefire extension points:
- access
- connection
- web terminal
- authentication service
- state change processor
- action processor
- transition processor
- condition processor
- app
- worker
- resource streamer
A typical cartridge development flow looks like this:
- Creating a new cartridge
- Creating denotation and expert types
- Implementing logic
- Wiring them together
- Testing
- Deploying the cartridge to a tribefire instance
This tutorial assumes you have set up your IDE and the Simple Cartridge as per the instructions here: Setting Up IDE.
Creating a New Cartridge
The easiest way of creating a new cartridge is to copy the structure of one of the enablement cartridges (Simple or Demo Cartridge).
Not only does this save your time, but also reduces the risk of potential errors connected to creating the cartridge structure all by yourself.
- Open the installation folder of your enablement cartridges (the
artifacts
folder mentioned in Setting Up IDE). The parent folders of Simple and Demo Cartridges should be available inside. - Create a copy of one of the
parent
folders (either Demo or Simple Cartridge, depending on your preferences). Components of that folder will be the foundation of your new cartridge. - Open the newly created copy. Rename all folders inside it so that they match the name of your new cartridge (for example
tribefire-demo-cartridge
->access-cartridge
). - Open the newly created folder in your code editor. Use the find and replace function to rename all components accordingly (find
tribefire-demo
, replace withaccess
, following the example in point 3). - Run
mvn clean install
in the newly created folder. Your cartridge is now added to your local repository. - Run the Jinni command
jinni setup-local-tomcat-platform setupDependency=your.group.id:your-cartridge-setup#version installationPath=your_installation_target_location -verbose true
. Your cartridge is now installed in the target directory. - Open your platform, and verify that the cartridge has been installed.
You need to repeat the installation process starting from step 5 each time you add new changes.
Creating a Denotation Type
In this tutorial, you are extending tribefire by introducing a new access called ReadOnlyAccess
. This access behaves as all other accesses, but instead of saving changes done to the models, it displays an error message, effectively making it a simple read-only access. This chapter of the tutorial focuses on creating the deployment model for your access.
A deployment model, in the context of cartridges, is used to deliver the denotation types and other required entities to Control Center. The denotation types are linked to their implementations (called experts) using Wire and it is the experts who have the actual logic. Denotation types contain method headers only.
As the cartridge you create is based on the simple cartridge, some package names may reflect that.
- Create a new
ReadOnlyAccess
interface in theaccess-deployment-model
project, in thecom.braintribe.tribefire.cartridge.simple.model.deployment.access
package. - Make sure that your interface extends the
com.braintribe.model.accessdeployment.IncrementalAccess
interface. As each deployment model must inherit from theDeployable
interface, you must make sure to implement it explicitly or implicitly. This particularIncrementalAccess
class is the basic extension class you can use when introducing custom accesses so it already inherits from theDeployable
interface.
public interface ReadOnlyAccess extends IncrementalAccess
- Allow your new interface to have access to reflection by introducing the immutable variable
T
:
final EntityType<ReadOnlyAccess> T = EntityTypes.T(ReadOnlyAccess.class);
For more information on reflection, see Reflection.
- As you want your access to behave like a normal access but display an error message when trying to save, the easiest way is to introduce a delegate to your interface. You could, of course, write the implementation for all methods yourself, but let's keep it simple for the sake of this tutorial. So, basically, you want to have two new properties: the delegate (which does all the actual work apart from saving) and the error message to be displayed when trying to save. Introduce two String properties to your interface:
public static final String access = "access";
public static final String exceptionMessage = "exceptionMessage";
These are the properties which are visible in Control Center. Make sure that the variable name is the same as its value. The properties are declared as Strings, because the functionality which binds the denotation type (so your interface) to its expert type (actual implementation) expects String values.
- Specify the method headers for setters and getters for each of the properties:
void setAccess (IncrementalAccess access);
IncrementalAccess getAccess();
void setExceptionMessage (String input);
String getExceptionMessage();
Note that the input arguments for the setter and the return types for the getter methods are actual types, not Strings.
- Save your changes.
Creating an Expert
Now that you have your deployment model ready, it is time to create a Java class which contains the actual logic of what you want to do with the access, which is to have all the calls except for one delegated to an existing access. The one call that is not delegated to an existing access is the applyManipulation()
call. In cartridge context, expert types are Java classes which you use to introduce your custom logic. The expert types are linked to their denotation types using Wire. Expert types are regular Java classes.
- Create a new
ReadOnlyAccess
class in theaccess-cartridge
project. In this tutorial, we recommend to use thecom.braintribe.tribefire.cartridge.simple.deployables.access
package. - Make sure that your class implements the
com.braintribe.model.access.IncrementalAccess
interface.
public class ReadOnlyAccess implements IncrementalAccess
- Create two class fields respective to the two properties you implemented in your denotation type interface:
private IncrementalAccess delegate;
private String exceptionMessage;
- Implement getters and setters for the two fields:
@Required
public void setDelegate(IncrementalAccess delegate) {
this.delegate = delegate;
}
@Required
public void setExceptionMessage(String exceptionMessage){
this.exceptionMessage = exceptionMessage;
}
Make sure to place the
@Required
annotation before the methods.
- Implement all the methods from the
com.braintribe.model.access.IncrementalAccess
interface but override them so that your delegate does the work:
@Override
public String getAccessId() {
return delegate.getAccessId();
}
- Change the logic of the
applyManipulation()
method to throw an exception (with theexceptionMessage
field as the message body) when the method is called:
@Override
public ManipulationResponse applyManipulation(ManipulationRequest arg0) throws ModelAccessException {
throw new ModelAccessException(exceptionMessage);
}
This is effectively the logic of the whole service. All other methods are delegated to
IncrementalAccess
.
- Save your changes.
Wiring Denotation Type to the Expert Type
In a cartridge context, denotation types are Java interfaces with method headers that you use to model your extension point. Denotation types are linked to their implementations (called experts) using Wire, and it is the experts who have the actual logic. Denotation types contain method headers only.
For more information about Wire see Wire in Detail.
- In the
access-cartridge
project, in theDeployablesSpace
class of the.com.braintribe.cartridge.extension.wire.space
package, create a new publicreadOnlyAccess(ExpertContext<ReadOnlyAccess> context)
method. Make sure to put the denotation type (com.braintribe.tribefire.cartridge.simple.model.deployment.access.ReadOnlyAccess
) as thecontext
parameter of the method. You may need to put the fully qualified name of the class as the parameter.
public ReadOnlyAccess readOnlyAccess(ExpertContext<ReadOnlyAccess> context)
This method's return type is
com.braintribe.tribefire.cartridge.simple.deployables.access.ReadOnlyAccess
, so the expert type.
- Create a
denotationType
variable of the denotation type's fully qualified name,com.braintribe.tribefire.cartridge.simple.model.deployment.access.ReadOnlyAccess
(so the denotation type), and assign it the returned value of thecontext.getDeployable()
method.
com.braintribe.tribefire.cartridge.simple.model.deployment.access.ReadOnlyAccess denotationType = context.getDeployable();
- Create a new variable called
access
of the typecom.braintribe.tribefire.cartridge.simple.deployables.access.ReadOnlyAccess
and instantiate the expert type -com.braintribe.tribefire.cartridge.simple.deployables.access.ReadOnlyAccess
.
com.braintribe.tribefire.cartridge.simple.deployables.access.ReadOnlyAccess access = new com.braintribe.tribefire.cartridge.simple.deployables.access.ReadOnlyAccess();
- Bind the exception message property from the denotation type to the expert type:
access.setExceptionMessage(denotationType.getExceptionMessage());
- Bind the access property from the denotation type to the expert type by creating a new
delegate
variable of the typecom.braintribe.model.access.IncrementalAccess
, assigning an expert to the variable, and assigning the delegate to your bean:
com.braintribe.model.access.IncrementalAccess delegate = context.resolve(denotationType.getAccess(), com.braintribe.model.accessdeployment.IncrementalAccess.T);
access.setDelegate(delegate);
return access;
When you assign properties from a deployable to an expert, make sure to assign each property separately:
bean.setMyProperty1(deployable.getMyProperty1);
bean.setMyProperty2(deployable.getMyProperty2);
- Navigate to the
extensions()
method definition in theCustomCartridgeSpace
class of thecom.braintribe.cartridge.extension.wire.space
package. - Enable component proxying in the component provider space.
masterComponents.enableIncrementalAccessProxy(bean);
The line masterComponents.enableIncrementalAccessProxy(bean)
enables the proxying of IncrementalAccess.T
components, which means that this cartridge creates proxies for IncrementalAccess
components deployed to other cartridges, and therefore, is able to resolve them within its deployables::readOnlyAccess
, method.
You can use the various
enable...Proxy()
methods to enable proxying for specific component types exposed by the components space. For more details on the method variants, refer to the JavaDoc ofcom.braintribe.cartridge.common.wire.contract.ComponentProxyingEnablingContract
class, a super contract of bothCommonComponentsContract
andMasterComponentsContract
.
- Still in the
extensions()
method, bind theReadOnlyAccess
denotation type (com.braintribe.tribefire.cartridge.simple.model.deployment.access.ReadOnlyAccess
) to its deployable supplier:
bean.bind(ReadOnlyAccess.T)
.component(masterComponents.incrementalAccess())
.expertFactory(deployables::readOnlyAccess);
- Open the command line in the root folder of your cartridge and run
mvn clean install
.
Testing
In this tutorial, testing consists in creating the Read Only Access and making sure its functionality works the way we intended.
- Set up your local environment to use your cartridge by running the command from step 8 from the Creating a New Cartridge section of this tutorial.
- Start the server (
runtime/bin/startup.bat
), open Control Center and click the Custom Accesses link. - Create a new a Smood Access, for example
MyNewSmoodAccess
and assign it a model to operate on, for exampleUserModel
. Add some entries to your new access, for example User 1 and User 2. - Create a new Read Only Access, and assign it the following:
Parameter | Description | Example |
---|---|---|
exceptionMessage | The message displayed when trying to save the changes. | Cannot save changes - this access is read-only. |
access | The access our Read Only Access delegates calls to. You can select any access present in the system, like the MyNewSmoodAccess access you created before. | MyNewSmoodAccess |
cartridge | The cartridge where the denotation type of the access is stored. Even though this parameter is not marked as mandatory, your access will not work if you leave this parameter blank. | mynewcartridge.cartridge |
metaModel | The model this access operates on. As your Read Only Access forwards all but one request to a delegate access, assign the metamodel of the delegate access here. | UserModel |
- Click Apply and then Commit.
- Right-click your Read Only Access and select More->Deploy.
- Once the access is deployed, right-click your access again and select the Switch To option.
- In the Explorer window, search for the entities you created and try to change a property, for example name. Then, click Commit. You see an exception message which reads the value of your
exceptionMessage
parameter.
Note that the changes have not been applied, which means the logic you implemented works.
What's Next?
Now that you have your custom cartridge, it is time to package it as an platform asset. To do that, you must first configure a platform asset repository for your tribefire. For instructions on how to do that, perform the steps described in the following sections:
- Configuring a Platform Asset Repository
- Maven Repository for Deploy Transfer
- Maven Repository for Deploy Transfer
Once you performed the procedures above, continue with the Working with Platform Assets document.