USER MANUALS

Developing a Custom View Policy

A custom view policy is a Java class with some annotations that mark the class as a custom view 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.

To develop a custom view policy, add <DENODO_HOME>/lib/contrib/denodo-commons-custom.jar to the classpath of your project.

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

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

This custom view policy limits the number of concurrent queries that a user/role can execute over the same view or stored procedure. This custom view 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.

Once you have developed a custom view policy and imported it into Virtual DataPort, read the article How to debug Denodo custom extensions with Eclipse of the Denodo Knowledge Base to learn how to debug it.


To develop a custom view 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 view 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 view 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 view 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 view policy was assigned to: getElementType() and getElementName().

  • Database of the element that the custom view policy was assigned to: getElementDatabase().

  • Collection with the assigned tags to the element getElementTags().

  • Map with the assigned tags to the fields from the element getFieldsTags().

  • 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.

  • Extra authentication information, if it was provided by the authentication mechanism: getAdditionalAuthenticationInformation().

  • Provides a JMX connection to the Virtual DataPort server that the custom view 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.

  • To check if a log category is enabled: isLogLevelEnabled(...).

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

Note

The custom view 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 view 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 view 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 view 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.

  • To mask values from the previous example with a different masking, use the constructor which receives the default masking expression and a map with the masking configuration per field. For example, for mask “salary” with the default behavior and “bonus” with 0 value.

    String condition = "1 is null";
    Map<String, CustomMaskingExpression> sensitiveFields = new LinkedHashMap<>();
    sensitiveFields.put("salary", CustomMaskingExpression.DEFAULT);
    sensitiveFields.put("bonus", CustomMaskingExpression.SET_0);
    return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER,
                    CustomRestrictionPolicyFilterType.MASK_SENSITIVE_FIELDS_IF_ANY_USED, condition,
                    CustomMaskingExpression.REDACT, sensitiveFields);
    

    In this example, if the query uses the fields “salary” and/or “bonus”, the field “salary” will be set to the result of the REDACT expression for the column type and “bonus” will bet set to 0 in all the rows.

  • To force a limit on the number of rows obtained from a view even if there is an offset/fetch clause, set your desired QUERY_LIMIT and then set the boolean FORCE_STRICT_QUERY_LIMIT to true.

    this.context.setProperty("QUERY_LIMIT","50");
    this.context.setProperty("FORCE_STRICT_QUERY_LIMIT", String.valueOf(true));
    

    In this example, the QUERY_LIMIT is set to 50 and the FORCE_STRICT_QUERY_LIMIT to true. This means that querys won’t return more than the first 50 values. For example, if we make a query with offset 10 and fetch 60, the result will just show 40 rows.


If an input parameter of the custom view 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 view policies are the same as in custom functions.

Add feedback