-
Notifications
You must be signed in to change notification settings - Fork 173
Custom_Function_embedded_mode
A sample Java application demonstrates using custom functions with Blazegraph in an embedded mode. See Custom Function tutorial for more details. We will create a new function that checks solutions against an internal security validator. This type of function is useful if you want to limit the visibility of results based on the current user's credentials. For example, if you have the following dataset:
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX ex: <http://www.example.com/>
ex:document1 a ex:Document ;
ex:grantedTo ex:John , ex:Mary .
ex:document2 a ex:Document ;
ex:grantedTo ex:Mary .
We can register a custom function with uri:
<http://www.example.com/validate>
Then we can use this function in a SPARQL query:
SELECT ?doc {
?doc rdf:type <http://www.example.com/Document> .
filter(<http://www.example.com/validate>(<http://www.example.com/John>, ?doc)) .
}
You can download sample-customFunction-embedded application on Github here.
GlobalSecurityValidator at first call caсhes security information in a Map that for each user stores a list of documents granted to him. Validate function uses it to check availability of a document for a user.
public class GlobalSecurityValidator {
protected static final Logger log = Logger.getLogger(GlobalSecurityValidator.class);
private static final String GRANTED_DOCUMENTS = "select ?user ?doc {" + //
"?doc <http://www.example.com/grantedTo> ?user" + //
"}";
private final Map<Value, List<Value>> securityInfo = new HashMap<Value, List<Value>>();
public GlobalSecurityValidator(final Repository repo) {
final TupleQueryResult result;
try {
result = Utils.executeSelectQuery(repo, GRANTED_DOCUMENTS, QueryLanguage.SPARQL);
while(result.hasNext()){
BindingSet bs = result.next();
Binding user = bs.getBinding("user");
Binding document = bs.getBinding("doc");
if(securityInfo.containsKey(user)){
securityInfo.get(user).add(document.getValue());
}else{
List<Value> docs = new LinkedList<Value>();
docs.add(document.getValue());
securityInfo.put(user.getValue(), docs);
}
}
} catch (OpenRDFException e) {
log.error("Security info was not collected", e);
}
}
public boolean validate(final Value user, final Value document){
if(securityInfo.containsKey(user)){
if(securityInfo.get(user).contains(document)){
return true;
} else {
return false;
}
} else {
return false;
}
}
}
A filter function will always evaluate to TRUE (keep the solution) or FALSE (drop the solution). If you are writing a simple boolean filter, you can extends XSDBooleanIVValueExpression.
public class SecurityFilter extends XSDBooleanIVValueExpression
implements INeedsMaterialization {
private GlobalSecurityValidator validator;
/**
* Required deep copy constructor.
*
* @param op
*/
public SecurityFilter(final SecurityFilter op) {
super(op);
}
/**
* Required shallow copy constructor.
*
* @param args
* The function arguments.
* @param anns
* The function annotations.
*/
public SecurityFilter(final BOp[] args, final Map<String, Object> anns) {
super(args, anns);
}
/**
* The function needs two pieces of information to operate - the document to check
* and the user to check against.
* @param validator
*/
public SecurityFilter(
final IValueExpression<? extends IV> user,
final IValueExpression<? extends IV> document,
final GlobalAnnotations globals, GlobalSecurityValidator validator) {
this(new BOp[] { user, document }, XSDBooleanIVValueExpression.anns(globals));
this.validator = validator;
}
@Override
protected boolean accept(final IBindingSet bset) {
// get the bound term for the ?user var
final Value user = asValue(getAndCheckBound(0, bset));
// get the bound term for the ?document var
final Value document = asValue(getAndCheckBound(1, bset));
return validator.validate(user, document);
}
public Requirement getRequirement() {
return Requirement.SOMETIMES;
}
}
public static void registerCustomFunction(final Repository repo){
final URI myFunctionURI = new URIImpl("http://www.example.com/validate");
final FunctionRegistry.Factory securityFactory = new FunctionRegistry.Factory() {
public IValueExpression<? extends IV> create(
BOpContextBase context, GlobalAnnotations globals,
Map<String, Object> scalarValues,
ValueExpressionNode... args) {
// Validate your argument(s)
FunctionRegistry.checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class);
// Turn them into physical (executable) bops
final IValueExpression<? extends IV> user = AST2BOpUtility.toVE(context, globals, args[0]);
final IValueExpression<? extends IV> document = AST2BOpUtility.toVE(context, globals, args[1]);
final GlobalSecurityValidator securityValidator = new GlobalSecurityValidator(repo);
// Return your custom function.
return new SecurityFilter(user, document, globals, securityValidator);
}
};
FunctionRegistry.add(myFunctionURI, securityFactory);
}
public class SampleBlazegraphCustomFunctionEmbedded {
protected static final Logger log = Logger.getLogger(SampleBlazegraphCustomFunctionEmbedded.class);
/*
* Select all documents available to <http://www.example.com/John>
*/
public static final String QUERY = "SELECT ?doc " + //
"{ ?doc rdf:type <http://www.example.com/Document> . " + //
" filter(<http://www.example.com/validate>(<http://www.example.com/John>, ?doc)) . }";
public static void main(String[] args) throws OpenRDFException, IOException {
final Repository repo = createRepository();
registerCustomFunction(repo);
try{
repo.initialize();
/*
* Load data from resources
* src/main/resources/data.n3
*/
Utils.loadDataFromResources(repo, "data.n3", "");
final TupleQueryResult result = Utils.executeSelectQuery(repo, QUERY, QueryLanguage.SPARQL);
try {
while(result.hasNext()){
BindingSet bs = result.next();
log.info(bs);
}
} finally {
result.close();
}
} finally {
repo.shutDown();
}
}
public static Repository createRepository(){
final Properties props = new Properties();
props.put(Options.BUFFER_MODE, BufferMode.DiskRW);
props.put(Options.FILE, "/tmp/blazegraph/test.jnl");
final BigdataSail sail = new BigdataSail(props);
final Repository repo = new BigdataSailRepository(sail);
return repo;
}
public static void registerCustomFunction(final Repository repo){
final URI myFunctionURI = new URIImpl("http://www.example.com/validate");
final FunctionRegistry.Factory securityFactory = new FunctionRegistry.Factory() {
public IValueExpression<? extends IV> create(
BOpContextBase context, GlobalAnnotations globals,
Map<String, Object> scalarValues,
ValueExpressionNode... args) {
// Validate your argument(s)
FunctionRegistry.checkArgs(args, ValueExpressionNode.class, ValueExpressionNode.class);
// Turn them into physical (executable) bops
final IValueExpression<? extends IV> user = AST2BOpUtility.toVE(context, globals, args[0]);
final IValueExpression<? extends IV> document = AST2BOpUtility.toVE(context, globals, args[1]);
final GlobalSecurityValidator securityValidator = new GlobalSecurityValidator(repo);
// Return your custom function.
return new SecurityFilter(user, document, globals, securityValidator);
}
};
FunctionRegistry.add(myFunctionURI, securityFactory);
}
}