package org.xenei.jdbc4sparql.impl.rdf; import java.net.MalformedURLException; import java.net.URL; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xenei.jdbc4sparql.iface.Catalog; import org.xenei.jdbc4sparql.iface.NameFilter; import org.xenei.jdbc4sparql.iface.Schema; import org.xenei.jdbc4sparql.iface.name.CatalogName; import org.xenei.jena.entities.EntityManager; import org.xenei.jena.entities.EntityManagerFactory; import org.xenei.jena.entities.EntityManagerRequiredException; import org.xenei.jena.entities.MissingAnnotation; import org.xenei.jena.entities.ResourceWrapper; import org.xenei.jena.entities.annotations.Predicate; import org.xenei.jena.entities.annotations.Subject; import com.hp.hpl.jena.graph.Node; import com.hp.hpl.jena.graph.NodeFactory; import com.hp.hpl.jena.query.Query; import com.hp.hpl.jena.query.QueryExecution; import com.hp.hpl.jena.query.QueryExecutionFactory; import com.hp.hpl.jena.query.QueryFactory; import com.hp.hpl.jena.query.QuerySolution; import com.hp.hpl.jena.query.ResultSet; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.util.iterator.WrappedIterator; import com.hp.hpl.jena.vocabulary.RDFS; @Subject(namespace = "http://org.xenei.jdbc4sparql/entity/Catalog#") public class RdfCatalog implements Catalog, ResourceWrapper { public static class Builder implements Catalog { public static String getFQName(final String shortName) { return String.format("%s/instance/N%s", ResourceBuilder.getFQName(RdfCatalog.class), shortName); } private Model localModel; private URL sparqlEndpoint; private String name; private final Set<Schema> schemas = new HashSet<Schema>(); public Builder() { } public Builder(final RdfCatalog catalog) throws MalformedURLException { this(); setName(catalog.getShortName()); if (catalog.getSparqlEndpoint() != null) { setSparqlEndpoint(new URL(catalog.getSparqlEndpoint())); } if (catalog.localModel != null) { setLocalModel(catalog.localModel); } } public RdfCatalog build(final Model model) { if (model == null) { throw new IllegalArgumentException("Model may not be null"); } checkBuildState(); final Class<?> typeClass = RdfCatalog.class; final String fqName = getFQName(); final ResourceBuilder builder = new ResourceBuilder(model); final EntityManager entityManager = EntityManagerFactory .getEntityManager(); // create catalog graph resource Resource catalog = null; if (builder.hasResource(fqName)) { catalog = builder.getResource(fqName, typeClass); } else { catalog = builder.getResource(fqName, typeClass); catalog.addLiteral(RDFS.label, name); if (sparqlEndpoint != null) { catalog.addProperty( builder.getProperty(typeClass, "sparqlEndpoint"), sparqlEndpoint.toExternalForm()); } for (final Schema scm : schemas) { if (scm instanceof ResourceWrapper) { catalog.addProperty( builder.getProperty(typeClass, "schema"), ((ResourceWrapper) scm).getResource()); } } } // create RdfCatalog object from graph object try { final RdfCatalog retval = entityManager.read(catalog, RdfCatalog.class); model.register(retval.new ChangeListener()); retval.localModel = localModel != null ? localModel : ModelFactory.createMemModelMaker().createFreshModel(); return retval; } catch (final MissingAnnotation e) { RdfCatalog.LOG.error( String.format("Error building %s: %s", name, e.getMessage()), e); throw new RuntimeException(e); } } protected void checkBuildState() { if (name == null) { throw new IllegalStateException("Name must be set"); } if ((localModel == null) && (sparqlEndpoint == null)) { localModel = ModelFactory.createDefaultModel(); } } @Override public void close() { throw new UnsupportedOperationException(); } @Override public List<QuerySolution> executeLocalQuery(final Query query) { throw new UnsupportedOperationException(); } @Override public NameFilter<Schema> findSchemas(final String schemaNamePattern) { return new NameFilter<Schema>(schemaNamePattern, schemas); } private String getFQName() { return Builder.getFQName(name); } public Model getLocalModel() { return localModel; } @Override public CatalogName getName() { return new CatalogName(name); } @Override public Schema getSchema(final String schemaName) { final NameFilter<Schema> nf = findSchemas(schemaName); return nf.hasNext() ? nf.next() : null; } @Override public Set<Schema> getSchemas() { return schemas; } public Builder setLocalModel(final Model localModel) { this.localModel = localModel; return this; } public Builder setName(final String name) { this.name = StringUtils.defaultString(name); return this; } public Builder setSparqlEndpoint(final URL sparqlEndpoint) { this.sparqlEndpoint = sparqlEndpoint; return this; } @Override public String getShortName() { return getName().getCatalog(); } @Override public boolean isService() { return false; } @Override public Node getServiceNode() { return null; } } public class ChangeListener extends AbstractChangeListener<Catalog, RdfSchema> { public ChangeListener() { super(RdfCatalog.this.getResource(), RdfCatalog.class, "schemas", RdfSchema.class); } @Override protected void addObject(final RdfSchema t) { schemaList.add(t); } @Override protected void clearObjects() { schemaList = null; } @Override protected boolean isListening() { return schemaList != null; } @Override protected void removeObject(final RdfSchema t) { schemaList.remove(t); } } // the model that contains the sparql data. private Model localModel; private Set<Schema> schemaList; private static Logger LOG = LoggerFactory.getLogger(RdfCatalog.class); @Override public void close() { localModel = null; schemaList = null; } /** * Execute the query against the local Model. * * This is used to execute queries built by the query builder. * * @param query * @return The list of QuerySolutions. */ @Override public List<QuerySolution> executeLocalQuery(final Query query) { final QueryExecution qexec = QueryExecutionFactory.create(query, localModel); try { final ResultSet rs = qexec.execSelect(); final List<QuerySolution> retval = WrappedIterator.create(rs) .toList(); return retval; } catch (final Exception e) { RdfCatalog.LOG.error( "Error executing local query: " + e.getMessage(), e); throw e; } finally { qexec.close(); } } /** * Execute a jena query against the data. * * @param query * The query to execute. * @return The list of QuerySolutions. */ public List<QuerySolution> executeQuery(final Query query) { QueryExecution qexec = null; if (isService()) { qexec = QueryExecutionFactory.sparqlService(getSparqlEndpoint(), query); } else { qexec = QueryExecutionFactory.create(query, localModel); } try { return WrappedIterator.create(qexec.execSelect()).toList(); } finally { qexec.close(); } } /** * Execute a query against the data. * * @param queryStr * The query as a string. * @return The list of QuerySolutions. */ public List<QuerySolution> executeQuery(final String queryStr) { return executeQuery(QueryFactory.create(queryStr)); } @Override public NameFilter<Schema> findSchemas(final String schemaNamePattern) { return new NameFilter<Schema>(schemaNamePattern, readSchemas()); } public Set<Schema> fixupSchemas(final Set<Schema> schemas) { final Set<Schema> schemaList = new HashSet<Schema>(); for (final Schema schema : schemas) { schemaList.add(RdfSchema.Builder.fixupCatalog(this, (RdfSchema) schema)); } this.schemaList = schemaList; return schemaList; } public Model getLocalModel() { return localModel; } @Override public CatalogName getName() { return new CatalogName(getShortName()); } @Override @Predicate(impl = true) public Resource getResource() { throw new EntityManagerRequiredException(); } @Override public Schema getSchema(final String schemaName) { final NameFilter<Schema> nf = findSchemas(StringUtils .defaultString(schemaName)); return nf.hasNext() ? nf.next() : null; } @Override @Predicate(impl = true, type = RdfSchema.class, postExec = "fixupSchemas") public Set<Schema> getSchemas() { throw new EntityManagerRequiredException(); } @Override public Node getServiceNode() { return isService() ? NodeFactory.createURI(getSparqlEndpoint()) : null; } @Override @Predicate(impl = true, namespace = "http://www.w3.org/2000/01/rdf-schema#", name = "label") public String getShortName() { throw new EntityManagerRequiredException(); } @Predicate(impl = true, emptyIsNull = true) public String getSparqlEndpoint() { throw new EntityManagerRequiredException(); } @Override public boolean isService() { return getSparqlEndpoint() != null; } private Set<Schema> readSchemas() { if (schemaList == null) { schemaList = getSchemas(); } return schemaList; } @Override public String toString() { return getName().toString(); } }