. . .

Hook Classes

Entry points for the backend logic are the methods in hook classes. A hook class is generated for each class that is created through the Dashboard. The hook methods are called when an operation is performed on the concerned class by ApiOmat. This applies to

  • Post (Create a new object)

  • Get (Get an object by ID)

  • Put (Update an object by ID)

  • Delete (Delete an object by ID)

as well as to

  • GetAll (Get all objects by a query)

  • PostRef (Create a reference to an object)

  • DeleteRef (Delete an existing reference to an object)

  • DeleteAll (Delete objects by a query))

  • GetRef (Get an existing reference)

  • Auth (Authenticate object access)

Available Methods

Supposed, we declared a class named DataClass via Dashboard, the corresponding hook class generated by ApiOmat until version 2.1 is called DataClassHooks.java. In newer versions, starting with ApiOmat 2.2, the methods are split up into two different hook-classes DataClassHooksTransient.java and DataClassHooksNonTransient.java. These classes reflect the state of the transient flag. If your class is a transient class, the hooks from DataClassHooksTransient.java will be called, or the DataClassHooksNonTransient.java otherwise. The classes contain the following methods:


DataClassHooksTransient.java
public String doPost( DataClass obj, Request r )
public void doPut( DataClass obj, Request r )
public DataClass doGet( String foreignId, Request r )
public boolean doDelete( String foreignId, Request r )
public boolean doDeleteAll( String query, Request r )
public List<DataClass> doGetAll( String query, Request r )
public long doCountAll( String query, Request r )
public void doPostRef( Object referencedObject, String referenceName, Request r )
public void doDeleteRef( String refName, String refForeignId, Request r )
public <Z extends AbstractClientDataModel> List<Z> doGetRef( String refName, String query, Request r )
public boolean auth( String httpVerb, String modelName, String modelForeignId, String userNameOrEmail, String password, Request r )
DataClassHooksNonTransient.java
public void beforePost( DataClass obj, Request r )
public void afterPost( DataClass obj, Request r )
public void beforeGet( String obj, Request r )
public void afterGet( DataClass obj, Request r )
public boolean beforePut( DataClass objFromDB, DataClass obj, Request r )
public void afterPut( DataClass obj, Request r )
public boolean beforeDelete( DataClass obj, Request r )
public String beforeGetAll( String query, Request r )
public List<DataClass> afterGetAll( List<DataClass> objects, String query, Request r )
public boolean beforePostRef( DataClass obj, Object referencedObject, String referenceName, Request r )
public void afterPostRef( DataClass obj, Object referencedObject, String referenceName, Request r )
public boolean beforeDeleteRef( DataClass obj, Object referencedObject, String referenceName, Request r )
public void afterDeleteRef( DataClass obj, Object referencedObject, String referenceName, Request r )
public String beforeGetAllReferences( String query, String referenceName, Request request )
public <Z extends AbstractClientDataModel> List<Z> afterGetAllReferences( List<Z> objects, String query,
String referenceName, Request request )
 
public boolean auth( String httpVerb, String modelName, String modelForeignId, String userNameOrEmail, String password, Request r )

The logic to be executed has to be placed into the suitable method body and is executed when the specified action is called.
It is necessary to distinguish whether the class is marked as transient or not. In the case of a transient class, the methods starting with "do..." (first block of the listing above) are usable, whereas the remaining (second block) are applicable if the class is non-transient.

The auth method is an exception and gets executed in both cases.

This distinction is caused by the fact that the internal doPost, doGet, .. methods are replaced entirely by the hook methods of the respective module if it is transient. The developer is responsible for responding to operations, especially for assigning an ID to objects on creation.

However in the case of a non-transient class, it is possible to execute code before and after the execution of internal methods by means of the hook methods starting with "before..." and "after...". The execution is comparable to Server Code.

Since it is impossible to set static variables (e.g. a consistent logging object) for technical reasons, the belonging object is injected into the hook class by the setCallingModel method during the initialization of the object. This initializes the class variable model of the hook class which provides access to static methods such as searching, saving or deleting objects.


As of version 2.5, the old beforeGet method has been renamed to afterGet, as it is called after the object has been loaded from the database. The old beforeGet method currently still gets invoked, but it is marked as deprecated and we strongly recommend you to update this. Additionally, there is a new beforeGet method, which is really called before the object is loaded. We've also added a beforeGetAll and beforeGetAllReferences where you can modify the query which is passed to the database.

Using references

Regarding the behaviour of referenced classes, it is necessary to distinguish between transient and non-transient classes.

In transient classes there is no built-in way to access references to other classes, as there is no locally stored data for the transient class. In order to use objects´ references, the generated method doGetRef needs to be implemented fetching the referenced objects. The doGetRef method is called in two cases. The first case is the GET call over the REST-API on /yambas/rest/apps/{appName}/{moduleName}/{modelName}/{dataModelId}/{refName} , when the referencing class is transient. The second case, as of ApiOmat version 2.5.6 and later, is the call of the obj.get[RefName]() method in NativeModule code. Both cases will internally first call the doGet method of the data model with the given id first (to have the values for this.model filled) and then call the doGetRef method.

If using a non-transient class, the hook-methods enable you to access referenced objects by using obj.get[RefName](), except for methods in which no reference is present (beforePost, afterPost, beforePostRef, afterDeleteRef). The referenced Ids or foreignIds are stored on the non-transient class object. If the object has a non-transient reference, it will simply load the object from our internal storage. If the object has a transient reference, it will get the stored foreignId and call the doGet method of the referenced class to load the reference.

Recursive Call

It is possible to create an endless loop here. See section on recursive calls for details.

Authen tication

The method auth() can be used in both cases. It serves the purpose of authenticating access to a specific object by means of authentication against a third party system. It is implemented once in a class and is called by ApiOmat on each access to a class object. Keep in mind that in some cases (e.g. getting all models of one class) authentication will be attempted for each single object. Therefore you may wish to cancel this process in case of one failed authentication. In order to do so, throw an exception:

MyModule.AOM.throwAuthenticationException( "Authentication failed due to wrong credentials!" );

or

MyModule.AOM.throwException( Status.AUTHENTICATION_REJECTED.getStatusCode( ), "Authentication failed due to wrong credentials!" );

The method verifyRequest( httpVerb, request ) is available for every object, as well. It uses the internal authentication of ApiOmat and throws an exception in case of unauthorized access. Read more about authentication.

Update to new Hook Structure

If you have an old module, which contains and uses the old hook-class, the new hooks will be added to your modules' files after the up- and download on a new version. But nevertheless, ApiOmat will use the old hook-file until you update the @Model annotation in your DataClass.

Assume that you've the following @Model annotation in your DataClass:


Old model annotation
@Model( moduleName = "MyModule",
hooksClassName = "com.apiomat.nativemodule.mymodule.DataClassHooks",
isTransient = false, requiredUserRoleCreate=UserRole.User, requiredUserRoleRead=UserRole.User,
requiredUserRoleWrite=UserRole.Owner, restrictResourceAccess=false,
allowedRolesCreate={}, allowedRolesRead={},
allowedRolesWrite={}, allowedRolesGrant={})
public class DataClass extends AbstractClientDataModel implements IModel<DataClass>
{
...

You have to replace the hook information in that annotation with the information where to find the new hook-methods:

New model annotation
@Model( moduleName = "MyModule",
hooksClassNameTransient = "com.apiomat.nativemodule.mymodule.DataClassTransientHooks",
hooksClassNameNonTransient = "com.apiomat.nativemodule.mymodule.DataClassNonTransientHooks",
isTransient = false, requiredUserRoleCreate=UserRole.User, requiredUserRoleRead=UserRole.User,
requiredUserRoleWrite=UserRole.Owner, restrictResourceAccess=false,
allowedRolesCreate={}, allowedRolesRead={},
allowedRolesWrite={}, allowedRolesGrant={})
public class DataClass extends AbstractClientDataModel implements IModel<DataClass>
{
...

You can then simply copy the relevant parts of the code from your old hook class to the new hook classes.

Inheritance

It will also affect generated hook classes if inheritance is used. Here the topmost parent class lists all hook methods whereas the subclasses declare none of them. Thus, the subclasses behave as their parent classes. If this behaviour is to be changed, the respective hook methods have to be overridden using the @Override annotation.

Example: Class A is the class at the top of a hierarchy and class B inherits from it. AHooksNonTransient and AHooksTransient declare all hook methods. doGet keeps the default behaviour and returns null, doDelete and the remaining methods are filled with own code.

B should now behave identically but use the default behaviour in doDelete and a user-defined behaviour in doGet. Thus, only these two methods have to be declared and annotated with @Override in BHooksTransient.

Furthermore, inheritance of hook classes requires the particular parent class to be defined within a native module. If the inheriting module was dynamic before, you may have to add inheritance manually.

Example: ClassA is defined in ModuleA, while ClassB is defined in ModuleB. ModuleA is a dynamic module, which means it was never down- and uploaded before. If you now set ClassB inheritance from ClassA and start downloading ModuleB, the generated hook methods are not inheriting from ClassA's ones, as ModuleA is still dynamic and not native. If you are down+uploading ModuleA now (or making it native via the Dashboard button) and download ModuleB again, the hooks will be regenerated (if ModuleB is still dynamic) and now inheriting from ClassA's ones. If ModuleB is already native and the hooks are not inheriting from ClassA's ones, you will have to add the inheritance on your own.

When hook inheritance was generated successfully, you'll see, that ClassB's hook class is inheriting from ClassA's one:

public class ClassBHooksNonTransient<T extends ClassB> extends ClassAHooksNonTransient<ClassB>
{
}

Distinguish Customer and User requests

In versions 2.2.1 and higher the request handed to any hook-method holds an attribute isAccountRequest. This attribute tells you whether the requester is authorized through an AbstractAccount. This indicates that it is a customer requesting. Otherwise the request originates from an "ordinary" user of your backend.

Special notes regarding Hook methods

beforePost

When you have a non-transient class and use the beforePost method, you can call setters on the object that gets passed as parameter (as obj). Do not call save() afterwards! Saving the object is automatically done after the beforePost internally; calling manually save() would lead to a duplicate ID exception during the internal save() call.

beforePut

In the beforePut method, you can validate the values which have been set for your object or set additional values for specific fields. If the given object contains values which are assumed to be invalid, you can interrupt and abort the update process by returning true in the update process.

doGet

There is a special case for transient classes that inherit from the User class regarding the loadMe method in the frontend SDKs. Due to the lack of class and id information on the /apps/{appName}/models/me endpoint, the loadMe method will be overridden for the transient class that inherits from user and make a GET request to the /apps/{appName}/models/{moduleName}/{dataModelName}/{dataModelId} endpoint, handing the username as datamodelId. For that case, the doGet method is called and contains the username as foreignId parameter.

Invoking other modules' hooks

If you are using other modules within your Native Modules (see: Using other modules ) you may wish to have its hook-methods to be invoked if you are performing the respective actions on the module's objects. In order to achieve this you have to set the respective flag to the request prior to calling object-methods:

r.setHooksInUsedModulesEnabled( true );

Recursive calls

As it is possible to create endless loops within hook classes, we introduced a configurable limit for the call depth of hook methods. If this limit is exeeded the invocation of the hook will be aborted and a custom exception is thrown.
The limit can be set in the apiomat.yaml using the key yambas.limits.maxHookCallDepth.

For example, the system will end in such an endless loop, if you call the obj.post[RefName](refObject) method within the doGet method of the object. As the post[RefName] internally calls the doGet of the object itself, to get the latest data of the object to post the reference on it (while the doGet call then calls the post[RefName] again, which will then call the doGet again, which will...).

* link only available in Enterprise Documentation