Apache > ManifoldCF > Release Documentation
 

Writing transformation connectors

Writing a Transformation Connector

A transformation connector provides a mechanism by which content that has been fetched from a repository can be modified before it gets handed to a back-end repository for processing.

As is the case with all connectors under the ManifoldCF umbrella, a transformation connector consists of a single part, which is:

  • A class implementing an interface (in this case, org.apache.manifoldcf.agents.interfaces.ITransformationConnector)

Key concepts

The transformation connector abstraction makes use of, or introduces, the following concepts:

ConceptWhat it is
Configuration parametersA hierarchical structure, internally represented as an XML document, which describes a specific configuration of a specific transformation connector, i.e. how the connector should do its job; see org.apache.manifoldcf.core.interfaces.ConfigParams
Transformation connectionAn output connector instance that has been furnished with configuration data
Repository documentAn object that describes a document's contents, including raw document data (as a stream), metadata (as either strings or streams), and access tokens; see org.apache.manifoldcf.agents.interfaces.RepositoryDocument
Connection management/threading/pooling modelHow an individual output connector class instance is managed and used
Activity infrastructureThe framework API provided to specific methods allowing those methods to perform specific actions within the framework, e.g. recording activities; see org.apache.manifoldcf.agents.interfaces.IOutputAddActivity and org.apache.manifoldcf.agents.interfaces.IOutputRemoveActivity
Output specificationA hierarchical structure, internally represented as an XML document, which describes what a specific transformation connector should do in the context of a specific job; see org.apache.manifoldcf.agents.interfaces.OutputSpecification
Transformation version stringA simple string, used for comparison purposes, that allows ManifoldCF to figure out if an ingestion operation needs to be repeated as a result of changes to the output specification in effect for a document
Service interruptionA specific kind of exception that signals ManifoldCF that the transformation engine is unavailable, and gives a best estimate of when it might become available again; see org.apache.manifoldcf.agents.interfaces.ServiceInterruption

Implementing the Transformation Connector class

A very good place to start is to read the javadoc for the transformation connector interface. You will note that the javadoc describes the usage and pooling model for a connector class pretty thoroughly. It is very important to understand the model thoroughly in order to write reliable connectors! Use of static variables, for one thing, must be done in a very careful way, to avoid issues that would be hard to detect with a cursory test.

The second thing to do is to examine the provided transformation connector implementations. The only currently enabled connector is the null transformation connector, which demonstrate (to some degree) the sorts of techniques you will need for an effective implementation. You will also note that this connector extends a framework-provided transformation connector base class, found at org.apache.manifoldcf.agents.transformation.BaseTransformationConnector. This base class furnishes some basic bookkeeping logic for managing the connector pool, as well as default implementations of some of the less typical functionality a connector may have. For example, connectors are allowed to have database tables of their own, which are instantiated when the connector is registered, and are torn down when the connector is removed. This is, however, not very typical, and the base implementation reflects that.

Principle methods

The principle methods an implementer should be concerned with for creating an output connector are the following:

MethodWhat it should do
checkMimetypeIndexable()Decide whether a document with a given mime type is indexable or not
checkDocumentIndexable()Decide whether a file is indexable or not
checkLengthIndexable()Decide whether a document of a given length is indexable or not
checkURLIndexable()Decide whether a document with a given URL is indexable or not
getPipelineDescription()Use the supplied output specification to come up with an output version string
addOrReplaceDocument()Add or replace the specified document within the target repository, or signal if the document cannot be handled
outputConfigurationHeader()Output the head-section part of an output connection ConfigParams editing page
outputConfigurationBody()Output the body-section part of an output connection ConfigParams editing page
processConfigurationPost()Receive and process form data from an output connection ConfigParams editing page
viewConfiguration()Output the viewing HTML for an output connection ConfigParams object
outputSpecificationHeader()Output the head-section part of a Specification editing page
outputSpecificationBody()Output the body-section part of a Specification editing page
processSpecificationPost()Receive and process form data from a Specification editing page
viewSpecification()Output the viewing page for a Specification object

These methods come in three broad classes: (a) functional methods for doing the work of the connector; (b) UI methods for configuring a connection; and (c) UI methods for editing the output specification for a job. Together they do the heavy lifting of your connector. But before you can write any code at all, you need to plan things out a bit.

Choosing the form of the transformation version string

The output version string is used by ManifoldCF to determine whether or not the output specification or configuration changed in such a way as to require that the document be reprocessed. ManifoldCF therefore requests the output version string for any document that is ready for processing, and usually does not process the document again if the returned output version string agrees with the output version string it has stored.

Thinking about it more carefully, it is clear that what an output connector writer needs to do is include everything in the output version string that could potentially affect how the document gets ingested, save that which is specific to the repository connector. That may include bits of output connector configuration information, as well as data from the output specification. When it's time to ingest, it's usually the correct thing to do to obtain the necessary data for ingestion out of the output version string, rather than calculating it or fetching it anew, because that guarantees that the document processing was done in a manner that agrees with its recorded output version string, thus eliminating any chance of ManifoldCF getting confused.

Notes on connector UI methods

The crawler UI uses a tabbed layout structure, and thus each of the UI methods must properly implement the tabbed model. This means that the "header" methods above must add the desired tab names to a specified array, and the "body" methods must provide appropriate HTML which handles both the case where a tab is displayed, and where it is not displayed. Also, it makes sense to use the appropriate css definitions, so that the connector UI pages have a similar look-and-feel to the rest of ManifoldCF's crawler ui. We strongly suggest starting with one of the supplied connector's UI code, both for a description of the arguments to each method, and for some decent ideas of ways to organize your connector's UI code.

Please also note that it is good practice to name the form fields in your HTML in such a way that they cannot collide with form fields that may come from the framework's HTML or any specific repository connector's HTML. The OutputSpecification HTML especially may be prone to collisions, because within any given job, this HTML is included in the same page as HTML from the chosen repository connector.

Implementation support provided by the framework

ManifoldCF's framework provides a number of helpful services designed to make the creation of a connector easier. These services are summarized below. (This is not an exhaustive list, by any means.)

  • Lock management and synchronization (see org.apache.manifoldcf.core.interfaces.LockManagerFactory)
  • Cache management (see org.apache.manifoldcf.core.interfaces.CacheManagerFactory)
  • Local keystore management (see org.apache.manifoldcf.core.KeystoreManagerFactory)
  • Database management (see org.apache.manifoldcf.core.DBInterfaceFactory)

For UI method support, these too are very useful:

  • Multipart form processing (see org.apache.manifoldcf.ui.multipart.MultipartWrapper)
  • HTML encoding (see org.apache.manifoldcf.ui.util.Encoder)
  • HTML formatting (see org.apache.manifoldcf.ui.util.Formatter)

DO's and DON'T DO's

It's always a good idea to make use of an existing infrastructure component, if it's meant for that purpose, rather than inventing your own. There are, however, some limitations we recommend you adhere to.

  • DO make use of infrastructure components described in the section above
  • DON'T make use of infrastructure components that aren't mentioned, without checking first
  • NEVER write connector code that directly uses framework database tables, other than the ones installed and managed by your connector

If you are tempted to violate these rules, it may well mean you don't understand something important. At the very least, we'd like to know why. Send email to dev@manifoldcf.apache.org with a description of your problem and how you are tempted to solve it.