/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Feb 27, 2012
*/
package com.bigdata.rdf;
import info.aduna.lang.service.ServiceRegistry;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.ServiceLoader;
import org.apache.log4j.Logger;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.resultio.TupleQueryResultParserFactory;
import org.openrdf.query.resultio.TupleQueryResultParserRegistry;
import org.openrdf.query.resultio.TupleQueryResultWriterFactory;
import org.openrdf.query.resultio.TupleQueryResultWriterRegistry;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFParserFactory;
import org.openrdf.rio.RDFParserRegistry;
import org.openrdf.rio.RDFWriterFactory;
import org.openrdf.rio.RDFWriterRegistry;
/**
* This static class provides a hook which allows the replacement of services
* registered via the openrdf {@link ServiceRegistry} pattern which makes use of
* the same underlying pattern which is disclosed by the {@link ServiceLoader}.
* <p>
* The {@link ServiceRegistry} pattern provides a declarative association
* between a service and a service provider. The service declarations are
* located in <code>META-INF/services/</code> packages. Each file in such a
* package name the service interface and the contents of the file name the
* service provider(s). The set of all such service provides located in all such
* service packages is registered for a given service interface. For openrdf,
* the service provides are initially associated with a <i>key</i>, such as an
* {@link RDFFormat}, {@link QueryLanguage}, etc.
* <p>
* However, this service registration pattern does not support the specification
* of a <em>preferred</em> service provider. In the context of multiple service
* providers for the same <i>key</i> and service interface, there is no way to
* control which service provider will remain in the {@link ServiceRegistry}.
* <p>
* This effects things such as the bigdata extension for the RDF/XML parser
* which adds support for SIDs mode interchange and the interchange of
* {@link com.bigdata.rdf.model.StatementEnum} metadata.
* <p>
* This class is used to "hook" the various service registeries and force the
* use of the bigdata extension when it adds semantics not present in the base
* service provider implementation. For such "hooked" services providers, the
* service registry pattern using <code>META-INF/services</code> is not
* manditory, but following the pattern is never the less recommended.
*
* @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/439">Class
* loader problems </a>
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public class ServiceProviderHook {
private static final Logger log = Logger.getLogger(ServiceProviderHook.class);
public static final String NTRIPLES_PARSER_FACTORY = "com.bigdata.rdf.rio.ntriples.BigdataNTriplesParserFactory";
public static final String TURTLE_PARSER_FACTORY = "com.bigdata.rdf.rio.turtle.BigdataTurtleParserFactory";
public static final String TURTLE_WRITER_FACTORY = "com.bigdata.rdf.rio.turtle.BigdataTurtleWriterFactory";
public static final String JSON_WRITER_FACTORY = "com.bigdata.rdf.rio.json.BigdataSPARQLResultsJSONWriterFactory";
public static final String JSON_CONSTRUCT_WRITER_FACTORY = "com.bigdata.rdf.rio.json.BigdataSPARQLResultsJSONWriterForConstructFactory";
public static final String JSON_RESULT_PARSER_FACTORY = "com.bigdata.rdf.rio.json.BigdataSPARQLResultsJSONParserFactory";
public static final String JSON_CONSTRUCT_PARSER_FACTORY = "com.bigdata.rdf.rio.json.BigdataSPARQLResultsJSONParserForConstructFactory";
static private boolean loaded = false;
static {
/*
* Note: These MUST be declared before the forceLoad() call or they will
* be NULL when that method runs.
*/
TURTLE_RDR = new RDFFormat("Turtle-RDR",
Arrays.asList("application/x-turtle-RDR"),
Charset.forName("UTF-8"), Arrays.asList("ttlx"), true, false);
NTRIPLES_RDR = new RDFFormat("N-Triples-RDR",
"application/x-n-triples-RDR", Charset.forName("US-ASCII"),
"ntx", false, false);
JSON_RDR = new RDFFormat("SPARQL/JSON", Arrays.asList(
"application/sparql-results+json", "application/json"),
Charset.forName("UTF-8"), Arrays.asList("srj", "json"),
RDFFormat.NO_NAMESPACES, RDFFormat.SUPPORTS_CONTEXTS);
forceLoad();
}
/**
* The extension MIME type for RDR data interchange using the RDR extension
* of TURTLE.
*
* @see <a href="http://trac.blazegraph.com/ticket/1038" >RDR RDF parsers not
* always discovered </a>
* @see http://wiki.blazegraph.com/wiki/index.php/Reification_Done_Right
*/
public static final RDFFormat TURTLE_RDR;
/**
* The extension MIME type for RDR data interchange using the RDR extension
* of N-TRIPLES.
*
* @see <a href="http://trac.blazegraph.com/ticket/1038" >RDR RDF parsers not
* always discovered </a>
* @see http://wiki.blazegraph.com/wiki/index.php/Reification_Done_Right
*/
public static final RDFFormat NTRIPLES_RDR;
/**
* The extension MIME type for RDR aware data interchange of RDF and SPARQL
* result stes using JSON.
*/
public static final RDFFormat JSON_RDR;
/**
* This hook may be used to force the load of this class so it can ensure
* that the bigdata version of a service provider is used instead of the
* openrdf version. This is NOT optional. Without this hook, we do not have
* control over which version is resolved last in the processed
* <code>META-INF/services</code> files.
* <p>
* Note: We need to use a synchronized pattern in order to ensure that any
* threads contending for this method awaits its completion. It would not
* be enough for a thread to know that the method was running. The thread
* needs to wait until the method is done.
*/
synchronized static public void forceLoad() {
if (loaded)
return;
log.warn("Running.");
if (log.isInfoEnabled()) {
for (RDFFormat f : RDFFormat.values()) {
log.info("RDFFormat: before: " + f);
}
for (RDFParserFactory f : RDFParserRegistry.getInstance().getAll()) {
log.info("RDFParserFactory: before: " + f);
}
for (RDFWriterFactory f : RDFWriterRegistry.getInstance().getAll()) {
log.info("RDFWriterFactory: before: " + f);
}
for (TupleQueryResultWriterFactory f : TupleQueryResultWriterRegistry
.getInstance().getAll()) {
log.info("TupleQueryResultWriterFactory: before: " + f);
}
}
// /*
// * Force load of the openrdf service registry before we load our own
// * classes.
// */
// {
// final String className = "info.aduna.lang.service.ServiceRegistry";
// try {
// Class.forName(className);
// } catch (ClassNotFoundException ex) {
// log.error(ex);
// }
// }
//
// RDFFormat.register(NTRIPLES_RDR);
// RDFFormat.register(TURTLE_RDR);
/*
* Register our RDFFormats.
*
* Note: They are NOT registered automatically by their constructors.
*/
RDFFormat.register(TURTLE_RDR);
RDFFormat.register(NTRIPLES_RDR);
RDFFormat.register(JSON_RDR);
/*
* Force the class loader to resolve the register, which will cause it
* to be populated with the service provides as declared in the various
* META-INF/services/serviceIface files.
*
* Once that step is known to be complete, we override the service
* provider for RDF/XML.
*/
{
final RDFParserRegistry r = RDFParserRegistry.getInstance();
// RDR-enabled
r.add((RDFParserFactory) getInstanceForClass(NTRIPLES_PARSER_FACTORY));
assert r.has(((RDFParserFactory) getInstanceForClass(NTRIPLES_PARSER_FACTORY)).getRDFFormat());
// RDR-enabled
r.add((RDFParserFactory) getInstanceForClass(TURTLE_PARSER_FACTORY));
assert r.has(((RDFParserFactory) getInstanceForClass(TURTLE_PARSER_FACTORY)).getRDFFormat());
/*
* Allows parsing of JSON SPARQL Results with an {s,p,o,[c]} header.
* RDR-enabled.
*/
r.add((RDFParserFactory) getInstanceForClass(JSON_CONSTRUCT_PARSER_FACTORY));
}
{
final TupleQueryResultWriterRegistry r = TupleQueryResultWriterRegistry.getInstance();
// add our custom RDR-enabled JSON writer for SPARQL result sets.
r.add((TupleQueryResultWriterFactory) getInstanceForClass(JSON_WRITER_FACTORY));
}
{
final TupleQueryResultParserRegistry r = TupleQueryResultParserRegistry.getInstance();
// add our custom RDR-enabled JSON parser for SPARQL result sets.
r.add((TupleQueryResultParserFactory) getInstanceForClass(JSON_RESULT_PARSER_FACTORY));
}
// Ditto, but for the writer.
{
final RDFWriterRegistry r = RDFWriterRegistry.getInstance();
// r.add(new BigdataRDFXMLWriterFactory());
// RDR-enabled
r.add((RDFWriterFactory) getInstanceForClass(TURTLE_WRITER_FACTORY));
// RDR-enabled
r.add((RDFWriterFactory) getInstanceForClass(JSON_CONSTRUCT_WRITER_FACTORY));
}
// {
// final PropertiesParserRegistry r = PropertiesParserRegistry.getInstance();
//
// r.add(new PropertiesXMLParserFactory());
//
// r.add(new PropertiesTextParserFactory());
//
// }
//
// {
// final PropertiesWriterRegistry r = PropertiesWriterRegistry.getInstance();
//
// r.add(new PropertiesXMLWriterFactory());
//
// r.add(new PropertiesTextWriterFactory());
//
// }
if (log.isInfoEnabled()) {
for (RDFFormat f : RDFFormat.values()) {
log.info("RDFFormat: after: " + f);
}
for (RDFParserFactory f : RDFParserRegistry.getInstance().getAll()) {
log.info("RDFParserFactory: after: " + f);
}
for (RDFWriterFactory f : RDFWriterRegistry.getInstance().getAll()) {
log.info("RDFWriterFactory: after: " + f);
}
for (TupleQueryResultWriterFactory f : TupleQueryResultWriterRegistry.getInstance().getAll()) {
log.info("TupleQueryResultWriterFactory: after: " + f);
}
}
loaded = true;
}
protected static Object getInstanceForClass(final String className) {
try {
final Class <?> c = Class.forName(className);
final Constructor<?> cons = c.getConstructor();
final Object classInstance = cons.newInstance();
return classInstance;
} catch (NoSuchMethodException | SecurityException
| ClassNotFoundException | InstantiationException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException(className + " is not found in the classpath.");
}
}
// private static void registerFactory
}