Skip to content

QueryLanguageIntegration

Quintin Siebers edited this page Apr 14, 2015 · 1 revision

Introduction

Originally, ontopia shipped with a single query language implementation, namely tolog, Lately the need for the integration of multiple other language implementations (toma, tmql-draft, future tmql drafts) came up, and therefore a plugin-like architecture to integrate such language implementations has been included.

In the following sections, we describe what has to be done by a developer in order to plug-in his implementation into ontopia.

Configuration

To add a new query language implementation to ontopia and take use of its plugin-like architecture, one has to implement specific interfaces described below.

Please note, that there are more interfaces (see package net.ontopia.topicmaps.query.core) that have to be supported by a full implementation, but will not be discussed here.

Interfaces

QueryProcessorIF

Interface for the actual query processor itself. Each query language will require at least one implementation of this interface. It provides methods to execute normal and update queries, see an excerpt of the interface below:

public interface QueryProcessorIF {
  /**
   * PUBLIC: Parses and executes the query, returning the results.
   */
  public QueryResultIF execute(String query)
    throws InvalidQueryException;

  /**
   * PUBLIC: Runs the update statement, returning the number of
   * modified objects.
   */
  public int update(String query) throws InvalidQueryException;
}

QueryProcessorFactoryIF

Each query language implementation will also need to implement a factory that is able to create a specific QueryProcessorIF instance based on the context. This serves mainly two purposes:

  • enable the creation of different QueryProcessors based on the context of the topic map input (e.g. for tolog there are different QueryProcessorIF implementations for topic maps that are stored in memory or rdbms backends).
  • support an automatic service discovery approach for available query languages, thus allowing to plug in vendor or 3rd party query language implementations.

The interface itself is very straightforward (see below), and contains only two methods:

/**
 * PUBLIC: Interface for query language implementations. An instance of a
 * {@link QueryProcessorFactoryIF} create an appropriate
 * {@link QueryProcessorIF} for the provided {@link TopicMapIF}.
 * 
 * @since 5.1
 */
public interface QueryProcessorFactoryIF {
  /**
   * PUBLIC: Returns the query language that is used by this
   * {@link QueryProcessorFactoryIF} implementation.
   * 
   * @return the name of this {@link QueryProcessorFactoryIF} implementation.
   */
  public String getQueryLanguage();

  /**
   * PUBLIC: Creates a new {@link QueryProcessorIF} instance to query a given
   * topic map.
   * 
   * @param topicmap the topic map to be used by the query processor.
   * @param base base address of the topic map if known.
   * @param properties additional properties used to configure the query
   *          processor.
   * @return a {@link QueryProcessorIF} instance that can be used to query the
   *         topic map.
   */
  public QueryProcessorIF createQueryProcessor(TopicMapIF topicmap,
      LocatorIF base, Map<String, String> properties);
}

Service Descriptor

In order to know which query language implementations are available at runtime, the engine uses an automatic service discovery mechanism following the Java Service Provider specification.

For each query language implementation, a single configuration file is required, that has to be placed into 'META-INF/services/' and is named 'net.ontopia.topicmaps.query.core.QueryProcessorFactoryIF'.

This file contains the full-qualified classname of the factory that is able to create an instance of a QueryProcessorIF for that specific query language (depending on the context, e.g. whether the topic map to be used for querying is stored in-memory or in an rdbms backend). If the jar, provides more than one query language implementation, multiple factories can be configured in this file, one on each line.

Using the Query Processor

The default language implementation used by the ontopia engine is 'tolog'. To create an instance of a QueryProcessorIF for a specific topic map, there exists a convenience class, net.ontopia.topicmaps.query.utils.QueryUtils. This class automatically scans the classpath for available query language implementations, and provides an interface to access them:

  /**
   * PUBLIC: Returns all available query language implementations.
   * 
   * @return a {@link Collection} of all available query languages.
   * @since 5.1
   */
  public static Collection<String> getAvailableQueryLanguages() {
    return qpFactoryMap.keySet();
  }

  /**
   * PUBLIC: Returns the {@link QueryProcessorFactoryIF} instance associated
   * with a specific query language. If the language is not available, null will
   * be returned. 
   * 
   * @param language the query language to be used (case insensitive).
   * @return the {@link QueryProcessorFactoryIF} instance for this language, or
   *         null, if not available.
   * @since 5.1
   */
  public static QueryProcessorFactoryIF getQueryProcessorFactory(String language) {
    return qpFactoryMap.get(language.toUpperCase());
  }

Using the getQueryProcessorFactory method, one can get the appropriate factory for each query language, and create QueryProcessorIF instances for provided topic maps. The rest of the methods in this class are for convenience and backwards-compatibility and access or create QueryProcessorIF instances of the default language (which is tolog).

Clone this wiki locally