A Java filter consists of two parts: a JSON file to describe the filter and any parameters it might take, and one or more Java classes that implement the filter.
Please download the sample project -- it contains everything you need to get started.
The filter is described in a JSON file, which looks like:
{
"active": true,
"codeType": "java",
"implementation": "com.acme.filter.MyFilter",
"phase": null,
"canHaveCode": false,
"helpURL": "https://www.acme.com/docs/MyFilter",
"tags": [],
"parameters": {
"My parameter": {
"defaultValue":null,
"description":"This parameter does this and that",
"type":"STRING",
"required":false,
"allowedValues": ["yes", "no", "maybe"]
}
}
}
The value of implementation should be the fully qualified name of the main Java class for the filter (the one that implements one of the Filter interfaces).
Parameters are not required, but they can be specified here. For each parameter:
Valid values for type are: STRING, INTEGER, BOOLEAN.
If defaultValue is specified, then the parameter will have that value if the user leaves it empty.
If allowedValues is specified, the parameter will be shown as a dropdown.
This JSON file must be named filter.json and must be installed in the Gallium Data metarepo directory under filter_types/<database>/<filter-type>/<filter-name>
For instance:
filter_types/MySQL/request_filters/MyJavaFilter/filter.json
A Java filter is a Java class that implements one of these interfaces:
You need to implement both RequestFilter and ResponseFilter to create a duplex filter.
This class will be invoked by Gallium Data in two ways:
when a filter is invoked for the first time, Gallium Data will invoke its configure method so that the filter can initialize itself, usually by reading the values of the parameters as set in the UI.
the filter will then be invoked whenever necessary: Gallium Data will call the filter method and pass it a context object containing everything the filter needs.
The Java class will need to use the following libraries (here in Maven form):
<dependency>
<groupId>galliumdata</groupId>
<artifactId>galliumdata-filter-library</artifactId>
<version>1.7.1-1468</version>
</dependency>
<dependency>
<groupId>org.graalvm.sdk</groupId>
<artifactId>graal-sdk</artifactId>
<version>22.0.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.2</version>
</dependency>
Note that the exact versions will depend on which version of Gallium Data you are using.
In addition, you'll need to add the following:
<repositories>
<repository>
<id>gallium-repo</id>
<url>https://maven.galliumdata.com/</url>
</repository>
</repositories>
This will allow Maven to find the galliumdata:galliumdata-filter-library jar.
Before Gallium Data invokes a filter, it calls its configure method so that the filter has an opportunity to configure itself.
This method has the following signature:
public void configure(FilterUse filterUse)
The FilterUse object contains all the required information about the filter, in particular the value of its parameters (if any). A typical filter will retrieve the values of its parameters and set things up accordingly, for instance:
public class ExampleFilter implements RequestFilter {
private Pattern userPattern;
@Override
public void configure(FilterUse filterUse) {
String userPatternStr = (String)filterUse.getParameters().get("User pattern");
if (userPatternStr != null) {
this.userPattern = Pattern.compile(userPatternStr);
}
etc...
If the configuration is expensive, you can cache objects in the filterContext:
Variables filterContext = filterUse.getFilterContext();
if (filterContext.containsKey("expensiveThing") {
// Initialization already done -- reuse the object
this.expensiveThing = (ExpensiveThing)filterContext.get("expensiveThing");
}
else {
// Do the expensive initialization
this.expensiveThing = ...
}
The name of the filter method is:
acceptConnection for connection filters
filterRequest for request filters
filterResponse for response filters
The filter method is invoked with a parameter of type Variables, which contains everything the filter needs. This is the same object that JavaScript code receives as the context object.
The filter method returns a FilterResult object, which must not be null. Returning an empty FilterResult object (i.e. new FilterResult() ) means that execution should continue. Setting the properties of that FilterResult object can change what Gallium Data does with the request. See the Javadoc for FilterResult for details.
Connection filters must implement the method:
public FilterResult acceptConnection(Socket socket, Variables context)
The Socket object is passed in so that your filter can read or write from it if needed.
The context object contains:
log: a Logger object
filterContext: a Variables object, which is the context for this filter -- anything defined in this context will be visible by all invocations of this filter, and will remain as long as the filter is active.
connectionContext: a Variables object, which will be shared by all invocations of all filters for the database connection that is being opened.
Request filters must implement the filterRequest method:
FilterResult filterRequest(Variables context)
The context object contains:
packet: the request object, can be any of a number of types, but most database connectors define a common supertype. For instance, all MySQL requests will be a subclass of MySQLPacket.
log: a Logger object
filterContext: a Variables object, which is the context for this filter -- anything defined in this context will be visible by all invocations of this filter, and will remain as long as the filter is active.
connectionContext: a Variables object, which will be shared by all invocations of all filters for the database connection that is being opened.
Response filters must implement the filterResponse method:
FilterResult filterResponse(Variables context)
The context object contains:
packet: the response object, can be any of a number of types, but most database connectors define a common supertype. For instance, all MySQL responses will be a subclass of MySQLPacket.
log: a Logger object
filterContext: a Variables object, which is the context for this filter -- anything defined in this context will be visible by all invocations of this filter, and will remain as long as the filter is active.
connectionContext: a Variables object, which will be shared by all invocations of all filters for the database connection that is being opened.
Once you have a Java filter that compiles properly, see: