Programmer : Connector Programmer : Push API Connector Framework : Extending the Files Connector through Plugins
 
Extending the Files Connector through Plugins
 
Technical Overview
First Method
Second Method
The filesystem connector embeds natively a number of schemes and protocols: native filesystem, Windows share filesystem (\\path or smb:// URLs), basic ftp support (ftp:// URLs), basic http (http://), etc.
It is possible to extend the features of the filesystem connector and use all the embedded features of the connector (multithreaded scan, containers handling, etc.) without having to create a new connector, by implementing additional protocol schemes through plugins.
Note: For a description of the Files Connector features, see "Files Connector" in the Exalead CloudView Connectors Guide.
Technical Overview
A filesystem connector interface plugin is a regular plugin, providing a factory component without any associated configuration.
The main filesystem connector interface component must implement FileInterfaceFactory (com.exalead.papi.connectors.filesystem.FileInterfaceFactory) and, as usual, CVComponent (com.exalead.mercury.component.CVComponent). It must provide a default constructor (no arguments).
Two methods must be implemented.
First Method
The first method allows you to define supported root path schemes, that is to say, whether the root path is recognized by this plugin.
/**
* Test whether a root path is handled by this factory;
* i.e. if build() may be called upon this path.
* The factory needs to ensure the namespace used will not conflict with
* any native namespace, of with previous plugin.
*
* @param rootConf
* The root path.
* @return true if the root path is handled by this factory.
*/
public boolean canHandle(final FilesystemRootPathConfig rootConf);
This method must return true if the root path passed is recognized by the plugin. It will typically check the syntax of rootConf.getRootKey() against a known specific URL scheme prefix.
Important: Make sure that no other plugin is using this prefix, or the filesystem connector will raise an error due to the namespace conflict.
For example, when using "myfile://" as prefix for root paths, you may use:
@Override
public boolean canHandle(FilesystemRootPathConfig rootConf) {
final boolean handle = rootConf.getRootKey().startsWith("myfile://");
return handle;
}
Second Method
The second method will provide an instance of FileInterface (com.exalead.papi.connectors.filesystem.FileInterface) to handle the virtual underlying filesystem. This method will only be called by the framework if canHandle() returned true upon the same configuration object.
/**
* Build a new FileInterface
*
* @param rootConf
* the root path
* @return The FileInterface
* @throws IOException
* Upon I/O error during object creation
* @throws IllegalArgumentException
* If the root path is unsupported (ie. canHandle() would have
* returned false)
*/ public FileInterface build(final FilesystemRootPathConfig rootConf) throws IOException,
IllegalArgumentException;
The object passed provides the root key (getRootKey()) and authentication details if needed.
For example, when using "myfile://" as prefix for root paths, you may use:
@Override
public FileInterface build(FilesystemRootPathConfig rootConf) throws IOException, IllegalArgumentException {
if (!canHandle(rootConf)) { // unexpected
throw new IllegalArgumentException("unsupported scheme");
}
final File root = new
File(rootConf.getRootKey().replace("myfile://", ""));
return new MyFileInterface(root);
}
The FileInterface (com.exalead.papi.connectors.filesystem.FileInterface) interface provides the necessary functions to handle a virtual filesystem (listing the directory, opening a file, fetching attributes, etc.):
package com.exalead.papi.connectors.filesystem;

import java.io.IOException;
import java.util.Iterator;

import com.exalead.papi.helper.Meta;
import com.exalead.papi.helper.stream.ContentStreamSafe;

/** * Abstract file interface. */
public interface FileInterface {
/**
* Get the absolute path.
*
* @return The absolute path.
*/
public String getAbsolutePath();
/**
* Is the file a file ? *
* @return true if this is a file
*/
public boolean isFile();
/**
* Is the file a directory ?
*
* @return true if this is a directory
*/
public boolean isDirectory();
/**
* Is the file a link ?
*
* @return true if this is a link
*/
public boolean isLink();
/**
* Get the path leaf name.
*
* @return the path leaf name
*/
public String getName();
/**
* Last-modified date.
*
* @return Last-modified date, or 0 if not supported.
*/
public long lastModified();
/**
* Return the time when the file was last accessed (in milliseconds since
* Epoch)
*
* @return Last-access date, or 0 if not supported.
**/
public long lastAccess();
/**
* Return the time when the file was created (in milliseconds since Epoch)
* Return 0 if this attribute if unsupported by the filesystem.
*
* @return Creation date, or 0 if not supported.
**/
public long creation();
/**
* The file length.
*
* @return file length
*/
public long length();
/**
* Does the file exist?
*
* @return true if the file exists
*/
public boolean exists();
/**
* Is the file readable?
*
* @return true if the file is readable
*/
public boolean canRead();
/**
* Get security meta-data.
*
* @return security meta-data
*/
public Meta[] getSecurityMetas() throws IOException;
/**
* Get additional meta-data.
*
* @return additional meta-data, or @c null if no additional meta-data are
* present.
*/
public Meta[] getAdditionalMetas() throws IOException;
/**
* Get contents.
*
* @return The stream contents.
* @throws Exception
* Upon error.
*/
public ContentStreamSafe getContents() throws Exception;
/**
* Enumerate files. Only available for directories.
*
* @return the iterator
*/
public Iterator<FileInterface> enumerateFiles(FileInterfaceFilterfilter) throws IOException;
/**
* Enumerate files. Only available for directories.
*
* @return the iterator, or @c null upon error
*/
public Iterator<FileInterface> enumerateFiles() throws IOException;
/**
* Get a child.
*
* @param name
* The child name.
* @return the child.
*/
public FileInterface getChild(String name); }
Example of a very basic implementation of a filesystem scheme (this sample is available in the sample list):
public class MyFileInterface implements FileInterface {
protected final File file;
public MyFileInterface(File file) {
this.file = file;
}

@Override
public boolean canRead() {
return file.canRead();
}

@Override
public long creation() {
return -1; // unsuppoorted
}

@Override
public Iterator<FileInterface> enumerateFiles(FileInterfaceFilterfilter) throws IOException {
final List<FileInterface> list = new ArrayList<FileInterface>();
for (final File f : file.listFiles()) {
final MyFileInterface child = new MyFileInterface(f);
if (filter == null || filter.accept(child)) {
list.add(child);
}
}
return list.iterator();
}

@Override
public Iterator<FileInterface> enumerateFiles() throws IOException {
return enumerateFiles(null);
}

@Override
public boolean exists() {
return file.exists();
}

@Override
public String getAbsolutePath() {
return file.getAbsolutePath();
}

@Override
public Meta[] getAdditionalMetas() throws IOException {
return new Meta[] { new Meta("canonical_path",file.getCanonicalPath()) };
}

@Override
public FileInterface getChild(String name) {
return new MyFileInterface(new File(file, name));
}

@Override
public ContentStreamSafe getContents() throws Exception {
return new MyContentStreamSafe(file);
}

@Override
public String getName() {
return file.getName();
}

@Override
public Meta[] getSecurityMetas() throws IOException {
return new Meta[] { SecurityMeta.getPublicSecurityMeta() };
}

@Override
public boolean isDirectory() {
return file.isDirectory();
}

@Override
public boolean isFile() {
return file.isFile();
}

@Override
public boolean isLink() {
return false;
}

@Override
public long lastAccess() {
return -1; // unsuppoorted
}

@Override
public long lastModified() {
return file.lastModified();
}

@Override
public long length() {
return file.length();
}
}