/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.apache.stanbol.rules.manager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Iterator;
import java.util.List;
import org.apache.clerezza.commons.rdf.Literal;
import org.apache.clerezza.commons.rdf.RDFTerm;
import org.apache.clerezza.commons.rdf.Triple;
import org.apache.clerezza.commons.rdf.Graph;
import org.apache.clerezza.commons.rdf.IRI;
import org.apache.clerezza.rdf.core.access.EntityAlreadyExistsException;
import org.apache.clerezza.rdf.core.access.NoSuchEntityException;
import org.apache.clerezza.rdf.core.access.TcManager;
import org.apache.clerezza.commons.rdf.impl.utils.PlainLiteralImpl;
import org.apache.clerezza.commons.rdf.impl.utils.TripleImpl;
import org.apache.clerezza.rdf.core.sparql.ParseException;
import org.apache.clerezza.rdf.core.sparql.QueryParser;
import org.apache.clerezza.rdf.core.sparql.ResultSet;
import org.apache.clerezza.rdf.core.sparql.SolutionMapping;
import org.apache.clerezza.rdf.core.sparql.query.Query;
import org.apache.clerezza.rdf.core.sparql.query.SelectQuery;
import org.apache.clerezza.rdf.ontologies.RDF;
import org.apache.clerezza.rdf.utils.UnionGraph;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.stanbol.rules.base.api.AlreadyExistingRecipeException;
import org.apache.stanbol.rules.base.api.NoSuchRecipeException;
import org.apache.stanbol.rules.base.api.NoSuchRuleInRecipeException;
import org.apache.stanbol.rules.base.api.Recipe;
import org.apache.stanbol.rules.base.api.RecipeConstructionException;
import org.apache.stanbol.rules.base.api.RecipeEliminationException;
import org.apache.stanbol.rules.base.api.Rule;
import org.apache.stanbol.rules.base.api.RuleStore;
import org.apache.stanbol.rules.base.api.Symbols;
import org.apache.stanbol.rules.base.api.util.RecipeList;
import org.apache.stanbol.rules.base.api.util.RuleList;
import org.apache.stanbol.rules.manager.parse.RuleParserImpl;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class provides an implementation of the {@link RuleStore} based on Clerezza.<br/>
* Recipe are managed as {@link Graph} graphs. <br/>
* The vocabulary used in these graphs is provided by {@link Symbols}.
*
* @author elvio
* @author anuzzolese
*
*/
@Component(immediate = true, metatype = true)
@Service(RuleStore.class)
public class ClerezzaRuleStore implements RuleStore {
@Reference
TcManager tcManager;
public static final String _RECIPE_INDEX_LOCATION_DEFAULT = "http://incubator.apache.org/stanbol/rules/recipe_index";
private final Logger log = LoggerFactory.getLogger(getClass());
@Property(name = RuleStore.RECIPE_INDEX_LOCATION, value = _RECIPE_INDEX_LOCATION_DEFAULT)
private String recipeIndexLocation;
private List<IRI> recipes;
/**
* This construct returns RuleStoreImpl object with inside an ontology where to store the rules.
*
* This default constructor is <b>only</b> intended to be used by the OSGI environment with Service
* Component Runtime support.
* <p>
* DO NOT USE to manually create instances - the RuleStoreImpl instances do need to be configured! YOU
* NEED TO USE {@link #ClerezzaRuleStore(Dictionary, TcManager)} or its overloads, to parse the
* configuration and then initialise the rule store if running outside a OSGI environment.
*/
public ClerezzaRuleStore() {}
/**
* To be invoked by non-OSGi environments. <br/>
* This construct returns an ontology where to store the rules.
*
* @param owl
* {OWLOntology object contains rules and recipe}
*/
public ClerezzaRuleStore(Dictionary<String,Object> configuration, TcManager tcManager) {
this();
try {
this.tcManager = tcManager;
} catch (Exception e) {
log.error(e.getLocalizedMessage(), e);
this.tcManager = null;
}
try {
// activator has a branch for existing owlfile
activate(configuration);
} catch (IOException e) {
log.error("Unable to access servlet context.", e);
}
}
/**
* Used to configure an instance within an OSGi container.
*
* @throws IOException
*/
@SuppressWarnings("unchecked")
@Activate
protected void activate(ComponentContext context) throws IOException {
log.info("in " + ClerezzaRuleStore.class + " activate with context " + context);
if (context == null) {
throw new IllegalStateException("No valid" + ComponentContext.class + " parsed in activate!");
}
activate((Dictionary<String,Object>) context.getProperties());
}
/**
* Should be called within both OSGi and non-OSGi environments.
*
* @param configuration
* @throws IOException
*/
protected void activate(Dictionary<String,Object> configuration) throws IOException {
if (recipeIndexLocation == null || recipeIndexLocation.trim().isEmpty()) {
String value = (String) configuration.get(RECIPE_INDEX_LOCATION);
if (value != null && !value.trim().isEmpty()) recipeIndexLocation = value;
else recipeIndexLocation = _RECIPE_INDEX_LOCATION_DEFAULT;
}
recipes = new ArrayList<IRI>();
Graph tripleCollection = null;
try {
tripleCollection = tcManager.getGraph(new IRI(recipeIndexLocation));
} catch (NoSuchEntityException e) {
tripleCollection = tcManager.createGraph(new IRI(recipeIndexLocation));
}
for (Triple triple : tripleCollection) {
IRI recipeID = (IRI) triple.getSubject();
recipes.add(recipeID);
}
log.info("Rule Store activated. It contains " + recipes.size() + " recipes.", this);
}
/*
* Moved form AddRecipe class. The AddRecipe should not be used anymore.
*/
@Override
public Recipe createRecipe(IRI recipeID, String recipeDescription) throws AlreadyExistingRecipeException {
Graph tripleCollection;
try {
// create the Graph in the TcManager
tripleCollection = tcManager.createGraph(recipeID);
} catch (EntityAlreadyExistsException e) {
throw new AlreadyExistingRecipeException(e.getMessage());
}
Triple recipeTriple = new TripleImpl(recipeID, RDF.type, Symbols.Recipe);
Graph recipeIndexGraph = tcManager.getGraph(new IRI(recipeIndexLocation));
recipeIndexGraph.add(recipeTriple);
if (recipeDescription != null && !recipeDescription.isEmpty()) {
Triple descriptionTriple = new TripleImpl(recipeID, Symbols.description, new PlainLiteralImpl(
recipeDescription));
tripleCollection.add(descriptionTriple);
recipeIndexGraph.add(descriptionTriple);
}
// add the recpe ID to the list of known recipes
recipes.add(recipeID);
return new RecipeImpl(recipeID, recipeDescription, null);
}
/**
*
* @param recipe
* the recipe
* @param RuleRule
* the rule in Rule syntax
*
* @return the recipe we the new rule.
*/
@Override
public Recipe addRuleToRecipe(Recipe recipe, Rule rule, String description) {
log.debug("Adding rule to recipe " + recipe);
log.info("Rule : " + rule.toString());
IRI recipeID = recipe.getRecipeID();
Graph tripleCollection = tcManager.getGraph(recipeID);
// add the rule object to the graph representation of the recipe by the TcManager
tripleCollection.add(new TripleImpl(recipeID, Symbols.hasRule, rule.getRuleID()));
/*
* extract the rule body and head and add them to the rule object existing in the graph representation
* of the recipe.
*/
String stanbolSyntax = rule.toString();
int indexOfLPar = stanbolSyntax.indexOf('[');
int indexOfRPar = stanbolSyntax.indexOf(']');
stanbolSyntax = stanbolSyntax.substring(indexOfLPar + 1, indexOfRPar);
String[] parts = stanbolSyntax.split("->");
String body = parts[0].trim();
String head = parts[1].trim();
tripleCollection.add(new TripleImpl(rule.getRuleID(), Symbols.ruleName, new PlainLiteralImpl(rule
.getRuleName())));
if (description != null && !description.isEmpty()) {
tripleCollection.add(new TripleImpl(rule.getRuleID(), Symbols.description, new PlainLiteralImpl(
description)));
}
tripleCollection.add(new TripleImpl(rule.getRuleID(), Symbols.ruleBody, new PlainLiteralImpl(body)));
tripleCollection.add(new TripleImpl(rule.getRuleID(), Symbols.ruleHead, new PlainLiteralImpl(head)));
if (description != null) {
rule.setDescription(description);
}
recipe.addRule(new RecipeRule(recipe, rule));
return recipe;
}
/**
*
* Parse the set of rules provided by the rulesStream parameter as Stanbol syntax rules and add them to
* the Recipe in the store.<br/>
* The recipe is a {@link Graph} managed by the {@link TcManager}.
*
*
* @param recipe
* {@link Recipe} the recipe
* @param rulesStream
* {@link InputStream} the rule in Stanbol syntax
*
* @return the recipe with the new rule.
*/
@Override
public Recipe addRulesToRecipe(Recipe recipe, InputStream rulesStream, String description) {
log.debug("Adding rule to recipe " + recipe);
IRI recipeID = recipe.getRecipeID();
String namespace = recipeID.toString().substring(1, recipeID.toString().length() - 1) + "/";
log.info("Rule Namespace is " + namespace);
RuleList ruleList = RuleParserImpl.parse(namespace, rulesStream).getRuleList();
for (Rule rule : ruleList) {
recipe = addRuleToRecipe(recipe, rule, description);
}
return recipe;
}
/**
*
* @param recipeIRI
* the IRI of the recipe
* @param stanbolRule
* the rule in Rule syntax
*/
@Override
public Recipe addRulesToRecipe(Recipe recipe, String stanbolRule, String description) {
IRI recipeID = recipe.getRecipeID();
String namespace = recipeID.toString().substring(1, recipeID.toString().length() - 1) + "/";
RuleList ruleList = RuleParserImpl.parse(namespace, stanbolRule).getRuleList();
for (Rule rule : ruleList) {
recipe = addRuleToRecipe(recipe, rule, description);
}
return recipe;
}
@Deactivate
protected void deactivate(ComponentContext context) {
log.info("in " + ClerezzaRuleStore.class + " deactivate with context " + context);
}
@Override
public Recipe getRecipe(IRI recipeID) throws NoSuchRecipeException, RecipeConstructionException {
log.info("Called get recipe for id: " + recipeID);
Graph recipeGraph = null;
/**
* Throw a NoSuchRecipeException in case of the TcManager throws a NoSuchEntityException with respect
* to IRI representing the recipe.
*/
try {
recipeGraph = tcManager.getGraph(recipeID);
} catch (NoSuchEntityException e) {
throw new NoSuchRecipeException(recipeID.toString());
}
Iterator<Triple> descriptions = recipeGraph.filter(null, Symbols.description, null);
String recipeDescription = null;
if (descriptions != null && descriptions.hasNext()) {
recipeDescription = descriptions.next().getObject().toString();
}
String query = "SELECT ?rule ?ruleName ?ruleBody ?ruleHead " + "WHERE { " + " " + recipeID.toString()
+ " " + Symbols.hasRule.toString() + " ?rule . " + " ?rule "
+ Symbols.ruleName.toString() + " ?ruleName . " + " ?rule "
+ Symbols.ruleBody.toString() + " ?ruleBody . " + " ?rule "
+ Symbols.ruleHead.toString() + " ?ruleHead . " + "}";
Query sparql;
try {
sparql = QueryParser.getInstance().parse(query);
ResultSet resultSet = tcManager.executeSparqlQuery((SelectQuery) sparql, recipeGraph);
StringBuilder stanbolRulesBuilder = new StringBuilder();
boolean firstIteration = true;
while (resultSet.hasNext()) {
SolutionMapping solutionMapping = resultSet.next();
RDFTerm nameResource = solutionMapping.get("ruleName");
RDFTerm bodyResource = solutionMapping.get("ruleBody");
RDFTerm headResource = solutionMapping.get("ruleHead");
StringBuilder stanbolRuleBuilder = new StringBuilder();
stanbolRuleBuilder.append(((Literal) nameResource).getLexicalForm());
stanbolRuleBuilder.append("[");
stanbolRuleBuilder.append(((Literal) bodyResource).getLexicalForm());
stanbolRuleBuilder.append(" -> ");
stanbolRuleBuilder.append(((Literal) headResource).getLexicalForm());
stanbolRuleBuilder.append("]");
if (!firstIteration) {
stanbolRulesBuilder.append(" . ");
} else {
firstIteration = false;
}
String stanbolSyntax = stanbolRuleBuilder.toString();
log.info("Rule content {}", stanbolSyntax);
stanbolRulesBuilder.append(stanbolSyntax);
}
String stanbolSyntax = stanbolRulesBuilder.toString();
RuleList ruleList = null;
if (!stanbolSyntax.isEmpty()) {
String namespace = recipeID.toString().substring(1, recipeID.toString().length() - 1) + "/";
ruleList = RuleParserImpl.parse(namespace, stanbolSyntax).getRuleList();
}
return new RecipeImpl(recipeID, recipeDescription, ruleList);
} catch (ParseException e) {
throw new RecipeConstructionException(e);
}
}
@Override
public List<IRI> listRecipeIDs() {
return recipes;
}
@Override
public RecipeList listRecipes() throws NoSuchRecipeException, RecipeConstructionException {
RecipeList recipeList = new RecipeList();
for (IRI recipeID : recipes) {
Recipe recipe;
try {
recipe = getRecipe(recipeID);
} catch (NoSuchRecipeException e) {
throw e;
} catch (RecipeConstructionException e) {
throw e;
}
recipeList.add(recipe);
}
log.info("The Clerezza rule store contains {} recipes", recipeList.size());
return recipeList;
}
@Override
public boolean removeRecipe(IRI recipeID) throws RecipeEliminationException {
// remove the recipe from the TcManager
try {
tcManager.deleteGraph(recipeID);
} catch (NoSuchEntityException e) {
throw new RecipeEliminationException(e);
}
Graph recipeIndexGraph = tcManager.getGraph(new IRI(recipeIndexLocation));
Triple triple = new TripleImpl(recipeID, RDF.type, Symbols.Recipe);
recipeIndexGraph.remove(triple);
// System.out.println("Recipes: " +recipes.size());
// remove the recipe ID from in-memory list
recipes.remove(recipeID);
return true;
}
@Override
public boolean removeRecipe(Recipe recipe) throws RecipeEliminationException {
return removeRecipe(recipe.getRecipeID());
}
@Override
public Recipe removeRule(Recipe recipe, Rule rule) {
Graph tripleCollection = tcManager.getGraph(recipe.getRecipeID());
// remove from the graph recipe all the triples having the ruleID as subject.
Iterator<Triple> triplesIterator = tripleCollection.filter(rule.getRuleID(), null, null);
while (triplesIterator.hasNext()) {
tripleCollection.remove(triplesIterator.next());
}
// remove from the graph recipe the triple recipeID hasRule ruleID
tripleCollection.remove(new TripleImpl(recipe.getRecipeID(), Symbols.hasRule, rule.getRuleID()));
recipe.removeRule(rule);
return recipe;
}
@Override
public Rule getRule(Recipe recipe, String ruleName) throws NoSuchRuleInRecipeException {
return recipe.getRule(ruleName);
}
@Override
public Rule getRule(Recipe recipe, IRI ruleID) throws NoSuchRuleInRecipeException {
return recipe.getRule(ruleID);
}
@Override
public List<IRI> listRuleIDs(Recipe recipe) {
return recipe.listRuleIDs();
}
@Override
public List<String> listRuleNames(Recipe recipe) {
return recipe.listRuleNames();
}
@Override
public RuleList listRules(Recipe recipe) {
return recipe.getRuleList();
}
@Override
public Graph exportRecipe(Recipe recipe) throws NoSuchRecipeException {
try {
return tcManager.getGraph(recipe.getRecipeID());
} catch (NoSuchEntityException e) {
throw new NoSuchRecipeException(recipe.toString());
}
}
@Override
public RecipeList findRecipesByDescription(String term) {
String sparql = "SELECT ?recipe " + "WHERE { ?recipe a " + Symbols.Recipe.toString() + " . "
+ "?recipe " + Symbols.description + " ?description . "
+ "FILTER (regex(?description, \"" + term + "\", \"i\"))" + "}";
Graph tripleCollection = tcManager.getGraph(new IRI(recipeIndexLocation));
RecipeList matchingRecipes = new RecipeList();
try {
SelectQuery query = (SelectQuery) QueryParser.getInstance().parse(sparql);
ResultSet resultSet = tcManager.executeSparqlQuery(query, tripleCollection);
while (resultSet.hasNext()) {
SolutionMapping solutionMapping = resultSet.next();
IRI recipeID = (IRI) solutionMapping.get("recipe");
try {
Recipe recipe = getRecipe(recipeID);
log.info("Found recipe {}.", recipeID.toString());
matchingRecipes.add(recipe);
log.info("Found {} matching recipes.", matchingRecipes.size());
} catch (NoSuchRecipeException e) {
// in this case go on in the iteration by fetching other matching recipes
} catch (RecipeConstructionException e) {
// in this case go on in the iteration by fetching other matching recipes
}
}
} catch (ParseException e) {
log.error("The sparql query contains errors: ", e);
}
return matchingRecipes;
}
@Override
public RuleList findRulesByName(String term) {
String sparql = "SELECT ?recipe ?rule ?description " + "WHERE { " + "?recipe " + Symbols.hasRule
+ " ?rule . " + "?rule " + Symbols.ruleName + " ?name . " + "?rule "
+ Symbols.description + " ?description . " + "FILTER (regex(?name, \"" + term
+ "\", \"i\"))" + "}";
List<IRI> recipeIDs = listRecipeIDs();
Graph[] tripleCollections = new Graph[recipeIDs.size()];
for (int i = 0; i < tripleCollections.length; i++) {
tripleCollections[i] = tcManager.getGraph(recipeIDs.get(i));
}
UnionGraph unionGraph = new UnionGraph(tripleCollections);
RuleList matchingRules = new RuleList();
try {
SelectQuery query = (SelectQuery) QueryParser.getInstance().parse(sparql);
ResultSet resultSet = tcManager.executeSparqlQuery(query, unionGraph);
while (resultSet.hasNext()) {
SolutionMapping solutionMapping = resultSet.next();
IRI recipeID = (IRI) solutionMapping.get("recipe");
IRI ruleID = (IRI) solutionMapping.get("rule");
Literal description = (Literal) solutionMapping.get("description");
try {
Recipe recipe = getRecipe(recipeID);
Rule rule = new RecipeRule(recipe, getRule(recipe, ruleID));
if (description != null) {
rule.setDescription(description.getLexicalForm());
}
matchingRules.add(rule);
} catch (NoSuchRecipeException e) {
// in this case go on in the iteration by fetching other matching recipes
} catch (RecipeConstructionException e) {
// in this case go on in the iteration by fetching other matching recipes
} catch (NoSuchRuleInRecipeException e) {
// in this case go on in the iteration by fetching other matching recipes
}
}
} catch (ParseException e) {
log.error("The sparql query contains errors: ", e);
}
return matchingRules;
}
@Override
public RuleList findRulesByDescription(String term) {
String sparql = "SELECT ?recipe ?rule ?description " + "WHERE { " + "?recipe " + Symbols.hasRule
+ " ?rule . " + "?rule " + Symbols.description + " ?description . "
+ "FILTER (regex(?description, \"" + term + "\", \"i\"))" + "}";
List<IRI> recipeIDs = listRecipeIDs();
Graph[] tripleCollections = new Graph[recipeIDs.size()];
for (int i = 0; i < tripleCollections.length; i++) {
tripleCollections[i] = tcManager.getGraph(recipeIDs.get(i));
}
UnionGraph unionGraph = new UnionGraph(tripleCollections);
RuleList matchingRules = new RuleList();
try {
SelectQuery query = (SelectQuery) QueryParser.getInstance().parse(sparql);
ResultSet resultSet = tcManager.executeSparqlQuery(query, unionGraph);
while (resultSet.hasNext()) {
SolutionMapping solutionMapping = resultSet.next();
IRI recipeID = (IRI) solutionMapping.get("recipe");
IRI ruleID = (IRI) solutionMapping.get("rule");
Literal description = (Literal) solutionMapping.get("description");
try {
Recipe recipe = getRecipe(recipeID);
Rule rule = new RecipeRule(recipe, getRule(recipe, ruleID));
if (description != null) {
rule.setDescription(description.getLexicalForm());
}
matchingRules.add(rule);
} catch (NoSuchRecipeException e) {
// in this case go on in the iteration by fetching other matching recipes
} catch (RecipeConstructionException e) {
// in this case go on in the iteration by fetching other matching recipes
} catch (NoSuchRuleInRecipeException e) {
// in this case go on in the iteration by fetching other matching recipes
}
}
} catch (ParseException e) {
log.error("The sparql query contains errors: ", e);
}
return matchingRules;
}
public static void main(String[] args){
InputStream inputStream;
try {
inputStream = new FileInputStream(new File("/Users/mac/Desktop/domain.rule"));
RuleList ruleList = RuleParserImpl.parse("http://www.prova.it/", inputStream).getRuleList();
for (Rule rule : ruleList) {
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}