/*
* 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.
*/
package org.apache.jena.hadoop.rdf.io.registry;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.RecordWriter;
import org.apache.jena.hadoop.rdf.types.QuadWritable;
import org.apache.jena.hadoop.rdf.types.TripleWritable;
import org.apache.jena.riot.Lang;
/**
* A registry which is used by various classes to dynamically select record
* readers and writers based on a provided {@link Lang}
* <p>
* Readers and writers are dynamically discovered using the Java
* {@link ServiceLoader} mechanism. This will look for files under
* {@code META-INF/services} named
* {@code org.apache.jena.hadoop.rdf.io.registry.ReaderFactory} and
* {@code org.apache.jena.hadoop.rdf.io.registry.WriterFactory}. This follows
* the standard {@linkplain ServiceLoader} format of provided one class name per
* line which implements the relevant interface.
* </p>
*
*/
public class HadoopRdfIORegistry {
private static Map<Lang, ReaderFactory> readerFactories = new HashMap<>();
private static Map<Lang, WriterFactory> writerFactories = new HashMap<>();
private static boolean init = false;
static {
init();
}
private static synchronized void init() {
if (init)
return;
// Dynamically load and register reader factories
ServiceLoader<ReaderFactory> readerFactoryLoader = ServiceLoader.load(ReaderFactory.class);
Iterator<ReaderFactory> readerFactoryIterator = readerFactoryLoader.iterator();
while (readerFactoryIterator.hasNext()) {
ReaderFactory f = readerFactoryIterator.next();
addReaderFactory(f);
}
// Dynamically load and register writer factories
ServiceLoader<WriterFactory> writerFactoryLoader = ServiceLoader.load(WriterFactory.class);
Iterator<WriterFactory> writerFactoryIterator = writerFactoryLoader.iterator();
while (writerFactoryIterator.hasNext()) {
WriterFactory f = writerFactoryIterator.next();
addWriterFactory(f);
}
init = true;
}
/**
* Resets the registry to the default configuration
*/
public static synchronized void reset() {
if (!init)
return;
init = false;
init();
}
/**
* Registers the reader factory for all the languages it declares itself as
* supporting
*
* @param f
* Reader factory
*/
public static void addReaderFactory(ReaderFactory f) {
if (f == null)
throw new NullPointerException("Factory cannot be null");
readerFactories.put(f.getPrimaryLanguage(), f);
for (Lang altLang : f.getAlternativeLanguages()) {
readerFactories.put(altLang, f);
}
}
/**
* Registers the writer factory for all the languages it declares itself as
* supporting
*
* @param f
* Writer factory
*/
public static void addWriterFactory(WriterFactory f) {
if (f == null)
throw new NullPointerException("Factory cannot be null");
writerFactories.put(f.getPrimaryLanguage(), f);
for (Lang altLang : f.getAlternativeLanguages()) {
writerFactories.put(altLang, f);
}
}
/**
* Gets whether there is a quad reader available for the given language
*
* @param lang
* Language
* @return True if available, false otherwise
*/
public static boolean hasQuadReader(Lang lang) {
if (lang == null)
return false;
ReaderFactory f = readerFactories.get(lang);
if (f == null)
return false;
return f.canReadQuads();
}
/**
* Gets whether there is a triple reader available for the given language
*
* @param lang
* Language
* @return True if available, false otherwise
*/
public static boolean hasTriplesReader(Lang lang) {
if (lang == null)
return false;
ReaderFactory f = readerFactories.get(lang);
if (f == null)
return false;
return f.canReadTriples();
}
/**
* Tries to create a quad reader for the given language
*
* @param lang
* Language
* @return Quad reader if one is available
* @throws IOException
* Thrown if a quad reader is not available or the given
* language does not support quads
*/
public static RecordReader<LongWritable, QuadWritable> createQuadReader(Lang lang) throws IOException {
if (lang == null)
throw new IOException("Cannot create a quad reader for an undefined language");
ReaderFactory f = readerFactories.get(lang);
if (f == null)
throw new IOException("No factory registered for language " + lang.getName());
if (!f.canReadQuads())
throw new IOException(lang.getName() + " does not support reading quads");
RecordReader<LongWritable, QuadWritable> reader = f.createQuadReader();
if (reader == null)
throw new IOException("Registered factory for " + lang.getName() + " produced a null triples reader");
return reader;
}
/**
* Tries to create a triple reader for the given language
*
* @param lang
* Language
* @return Triple reader if one is available
* @throws IOException
* Thrown if a triple reader is not available or the given
* language does not support triple
*/
public static RecordReader<LongWritable, TripleWritable> createTripleReader(Lang lang) throws IOException {
if (lang == null)
throw new IOException("Cannot create a triple reader for an undefined language");
ReaderFactory f = readerFactories.get(lang);
if (f == null)
throw new IOException("No factory registered for language " + lang.getName());
if (!f.canReadTriples())
throw new IOException(lang.getName() + " does not support reading triples");
RecordReader<LongWritable, TripleWritable> reader = f.createTripleReader();
if (reader == null)
throw new IOException("Registered factory for " + lang.getName() + " produced a null triples reader");
return reader;
}
/**
* Gets whether there is a quad writer available for the given language
*
* @param lang
* Language
* @return True if available, false otherwise
*/
public static boolean hasQuadWriter(Lang lang) {
if (lang == null)
return false;
WriterFactory f = writerFactories.get(lang);
if (f == null)
return false;
return f.canWriteQuads();
}
/**
* Gets whether there is a triple writer available for the given language
*
* @param lang
* Language
* @return True if available, false otherwise
*/
public static boolean hasTriplesWriter(Lang lang) {
if (lang == null)
return false;
WriterFactory f = writerFactories.get(lang);
if (f == null)
return false;
return f.canWriteTriples();
}
/**
* Tries to create a quad writer for the given language
*
* @param lang
* Language
* @param writer
* Writer
* @param config
* Configuration
*
* @return Quad writer if one is available
* @throws IOException
* Thrown if a quad writer is not available or the given
* language does not support quads
*/
public static <TKey> RecordWriter<TKey, QuadWritable> createQuadWriter(Lang lang, Writer writer,
Configuration config) throws IOException {
if (lang == null)
throw new IOException("Cannot create a quad writer for an undefined language");
WriterFactory f = writerFactories.get(lang);
if (f == null)
throw new IOException("No factory registered for language " + lang.getName());
if (!f.canWriteQuads())
throw new IOException(lang.getName() + " does not support writeing quads");
RecordWriter<TKey, QuadWritable> rwriter = f.<TKey> createQuadWriter(writer, config);
if (rwriter == null)
throw new IOException("Registered factory for " + lang.getName() + " produced a null triples writer");
return rwriter;
}
/**
* Tries to create a triple writer for the given language
*
* @param lang
* Language
* @param writer
* Writer
* @param config
* Configuration
* @return Triple writer if one is available
* @throws IOException
* Thrown if a triple writer is not available or the given
* language does not support triple
*/
public static <TKey> RecordWriter<TKey, TripleWritable> createTripleWriter(Lang lang, Writer writer,
Configuration config) throws IOException {
if (lang == null)
throw new IOException("Cannot create a triple writer for an undefined language");
WriterFactory f = writerFactories.get(lang);
if (f == null)
throw new IOException("No factory registered for language " + lang.getName());
if (!f.canWriteTriples())
throw new IOException(lang.getName() + " does not support writing triples");
RecordWriter<TKey, TripleWritable> rwriter = f.<TKey> createTripleWriter(writer, config);
if (rwriter == null)
throw new IOException("Registered factory for " + lang.getName() + " produced a null triples writer");
return rwriter;
}
}