Developing a Custom Policy

A custom policy is a Java class with some annotations that mark the class as a custom policy and indicate which method the Server has to execute to intercept the query before executing it.

Every time a custom policy is executed, the Server creates a new instance of the class. However, if this Java class has “static” attributes, their value will be the same across executions of the policy.

We strongly recommend using the Denodo4E plugin for Eclipse, to develop custom policies (see the file README in <DENODO_HOME>/tools/denodo4e).

To develop a custom policy, add the <DENODO_HOME>/lib/contrib/denodo-commons-commons-custom.jar file to the Classpath of your environment.

The Virtual DataPort API Javadoc provides the documentation for the classes and annotations of this API.

There is a sample custom policy in the directory <DENODO_HOME>/samples/vdp/customPolicies/

This custom policy limits the number of concurrent queries that a user/role can execute over the same view or stored procedure. This custom policy has one input parameter called “Limit”, which sets the maximum number of concurrent queries this user/role can execute.

The README file of this directory explains how to compile the example and import it into Virtual DataPort.


To develop a custom policy, create a Java class like this one:

import com.denodo.common.custom.annotations.*;
import com.denodo.common.custom.policy.*;

@CustomElement(name = "my_new_custom_policy", type = CustomElementType.VDPCUSTOMPOLICY)
public class MyDenodoCustomPolicy {

    @CustomContext
    CustomRestrictionPolicyContext context;

    @CustomExecutor
    public CustomRestrictionPolicyValue execute(
        @CustomParam(name = "parameter1", mandatory = false) String inputParameter {

       /*
        * This custom policy accepts all the queries that involve
        * the view over which this policy was assigned.
        */
        return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT);
   }
}

The attribute name of @CustomElement is the name of the custom policy.

With the attribute context, you can obtain information about the context of the query:

  • Query that Virtual DataPort received: getQuery().

  • Fields involved in the query. That is, the list of fields in the SELECT, WHERE, GROUP BY and HAVING clauses: getFieldsInQuery().

  • User account that executes the query: getCurrentUserName().

  • List of the effective roles of the user account that executes the query: getCurrentUserRoles(). That is, the roles granted directly to the user and the ones granted to the roles of the user, not the user itself.

  • User agent of to the connection that executes the query: getCurrentUserAgent().

  • Database where the query is executed: getCurrentDatabaseName(). This may be different from the database to which the view belongs. For example, if you are connected to database “db1” and you execute SELECT * FROM db2.customer, this method will return “db1”.

  • User / role to whom the custom policy was assigned: getPolicyCredentialsName() and getPolicyCredentialsType(). The latter method returns if the policy is assigned to a user or a role.

  • View or stored procedure that the custom policy was assigned to: getElementType() and getElementName().

  • Properties of the query. Invoke getProperty(...) to obtain the value of the property and setProperty(...) to change it. The available properties are the constants defined in the CustomRestrictionPolicyContext class: I18N_PROPERTY, SWAP_PROPERTY, etc.

  • Provides a JMX connection to the Virtual DataPort server that the custom policy can use to retrieve any data via JMX: getJmxConnection(). The JMX interface of Denodo provides information about the current status of the server. This would allow you to modify the behavior of the policy based on the load of the Denodo server, the number of concurrent queries, etc.

  • To log a message invoke the method log(...). The messages logged using this method are redirected to the log category com.denodo.vdb.catalog.view.CustomRestrictionPolicyContextImpl.

  • For the full list of options, see the Javadoc of the class CustomRestrictionPolicyContext

Note

The custom policy mechanism initializes the attribute context right before invoking the method execute; this attribute will be null if you try to access it from the constructor of the class.

To add parameters to the custom policy, add a parameter to the method execute and annotate it with @CustomParam(name = "<parameter_name>", mandatory = false). This annotation has two parameters:

  • name: the Administration Tool uses this value when displaying information about this custom policy to the users.

  • mandatory: boolean value that indicates if this parameter is mandatory or not.

Later, when you assign this policy to a user/role over a view, you will have to provide a value for the input parameters of the policy.

This Java class must have one and only one method annotated with @CustomExecutor (in this example, this is the execute method). When the execution engine loads a custom policy during the execution of a query, it will execute the method marked with this annotation, which has to return a CustomRestrictionPolicyValue object.

  • To accept a query “as is”, return this:

    return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT);
    
  • To reject a query, return this:

    return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.REJECT);
    
  • To add a condition to the query, return this:

    String condition = "title <> 'Chief Executive Officer'";
    return new CustomRestrictionPolicyValue(
                   CustomRestrictionPolicyType.ACCEPT_WITH_FILTER
                   , CustomRestrictionPolicyFilterType.REJECT_ROW
                   , condition
                   , null);
    

    In this example, the execution engine will add the condition “title <> ‘Chief Executive Officer’” to the query executed by the user.

  • To add a condition to query when one or more fields of a list of fields are used in the query, return the following. A field is considered to be used in a query if at least one of these clauses uses the field: SELECT, WHERE, GROUP BY, HAVING.

    String condition = "title <> 'Chief Executive Officer'";
    Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus"));
    return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER,
                   CustomRestrictionPolicyFilterType.REJECT_ROW_IF_ANY_SENSITIVE_FIELDS_USED
                   , condition
                   , sensitiveFields);
    

    In this example, if the query uses the fields “salary” and/or “bonus”, the condition “title <> ‘Chief Executive Officer’” will be added to the query. If the query does not use any of these fields, the condition will not be added to the query.

  • To add a condition to the query when all the fields of a list of fields are used in the query, return the following. A field is considered to be used in a query if at least one of these clauses uses the field: SELECT, WHERE, GROUP BY, HAVING.

    String condition = "title <> 'Chief Executive Officer'";
    Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus"));
    return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER,
                    CustomRestrictionPolicyFilterType.REJECT_ROW_IF_ALL_SENSITIVE_FIELDS_USED, condition,
                    sensitiveFields);
    

    In this example, if the query uses both fields “salary” and “bonus”, the condition “title <> ‘Chief Executive Officer’” will be added to the query. If the query does not use all of the fields, the condition will not be added to the query.

    Note that in this example, we use the constant REJECT_ROW_IF_ALL_SENSITIVE_FIELDS_USED and in the previous example, we use REJECT_ROW_IF_ANY_SENSITIVE_FIELDS_USED.

  • To mask the values of a list of fields in the rows that do not verify a certain condition, when one or more of these fields are used in the query, return the following:

    String condition = "title <> 'Chief Executive Officer'";
    Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus"));
    return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER,
                    CustomRestrictionPolicyFilterType.MASK_SENSITIVE_FIELDS_IF_ANY_USED, condition,
                    sensitiveFields);
    

    To mask a field, its value is set to null.

    In this example, if the query uses the fields “salary” and/or “bonus”, the fields of the list “sensitiveFields” (“salary” and “bonus”) will be set to NULL in the rows that do not verify the condition. In this example, in the rows whose field “title” is “Chief Executive Officer” (i.e. do not meet the condition), the fields “salary” and “bonus” will be NULL.

    If the query does not use any of these fields, no fields will be masked.

  • To mask the values of a list of fields in the rows that do not verify a condition, when all these fields are used in the query, return the following:

    String condition = "title <> 'Chief Executive Officer'";
    Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus"));
    return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER,
                    CustomRestrictionPolicyFilterType.MASK_SENSITIVE_FIELDS_IF_ALL_USED, condition,
                    sensitiveFields);
    

    To mask a field, its value is set to null.

    In this example, if the query uses the fields “salary” and “bonus”, the fields of the list “sensitiveFields” (“salary” and “bonus”) will be set to NULL in the rows that do not verify the condition. In this example, in the rows whose field “title” is “Chief Executive Officer” (i.e. do not meet the condition), the fields “salary” and “bonus” will be NULL.

    If the query does not use all of the fields, the condition will not be added to the query nor any of the fields will be masked.

  • To mask the values of some fields in all the rows of the result of the query (i.e. with the option MASK_SENSITIVE_FIELDS_IF_ALL_USED or MASK_SENSITIVE_FIELDS_IF_ANY_USED), use a condition that always evaluates to false. For example: 1 is null.

    String condition = "1 is null";
    Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus"));
    return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER,
                    CustomRestrictionPolicyFilterType.MASK_SENSITIVE_FIELDS_IF_ANY_USED, condition,
                    sensitiveFields);
    

    In this example, if the query uses the fields “salary” and/or “bonus”, the fields “salary” and “bonus” will bet set to NULL in all the rows.


If an input parameter of the custom policy is a datetime value, check the section Dealing with Datetime and Interval Types for custom functions. The rules to manage input parameters in custom policies are the same as in custom functions.