JniRepositoryPlace.java

package emissary.jni;

import emissary.core.IBaseDataObject;
import emissary.place.ServiceProviderPlace;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;

public class JniRepositoryPlace extends ServiceProviderPlace {

    protected List<String> libraryDirectoryString = new ArrayList<>();
    protected List<File> libraryDirectoryFile = new ArrayList<>();

    /**
     * The remote constructor
     */
    public JniRepositoryPlace(final String cfgInfo, final String dir, final String placeLoc) throws IOException {
        super(cfgInfo, dir, placeLoc);
        configurePlace();

        logger.info("JniRepository: Prioritized repository list: {}", this.libraryDirectoryString);
    }

    /**
     * The static standalone (test) constructor
     */
    public JniRepositoryPlace(final String cfgInfo) throws IOException {
        super(cfgInfo, "TestJniRepositoryPlace.host.domain.com:8003");
        configurePlace();
    }

    /**
     * Set up place specific information from the config file, load JNI, etc.
     */
    private void configurePlace() {

        // Set libraryDirectory from JniRepositoryPlace.cfg
        this.libraryDirectoryString = configG.findEntries("LIBRARY_DIRECTORY");
        if (this.libraryDirectoryString.isEmpty()) {
            logger.warn("JniRepository: No LIBRARY_DIRECTORY indicators.");
        }

        for (int i = 0; i < this.libraryDirectoryString.size(); i++) {
            this.libraryDirectoryFile.add(new File(this.libraryDirectoryString.get(i)));

            if (!this.libraryDirectoryFile.get(i).isDirectory()) {
                logger.warn("Invalid libraryDirectory: {} is not a directory.", this.libraryDirectoryString.get(i));
                this.libraryDirectoryFile.set(i, null);
            } else if (!this.libraryDirectoryFile.get(i).canRead()) {
                logger.warn("Invalid libraryDirectory: {} is not readable.", this.libraryDirectoryString.get(i));
                this.libraryDirectoryFile.set(i, null);
            }
        }
    }

    /**
     * Allow other places to ask if we have a certain library available.
     */

    public/* synchronized */boolean nativeLibraryQuery(final String query) {

        for (final File dir : this.libraryDirectoryFile) {
            if (dir == null) {
                continue;
            }

            final String[] fileList = dir.list((dir1, name) -> name.equals(query));

            if ((fileList != null) && (fileList.length > 0)) {
                return true;
            }
        }
        return false;
    }

    /**
     * The query string is the name of a native library to serve back. The requesting place is searching for a native
     * library (.so or .dll) to use. If we have what the requestor is looking for in our libraryDirectory, serve it back to
     * them as an array of bytes.
     */
    @Nullable
    public /* synchronized */ byte[] nativeLibraryDeliver(final String query) throws RemoteException {

        final File nativeLib = nativeLibraryLookup(query);

        if ((nativeLib == null) || !nativeLib.isFile() || !nativeLib.canRead()) {
            logger.warn("NativeLibraryDeliver did not find file {}", query);
            return null;
        }

        try (InputStream theFile = Files.newInputStream(nativeLib.toPath());
                DataInputStream theStream = new DataInputStream(theFile)) {
            final byte[] theContent = new byte[theStream.available()];
            theStream.readFully(theContent);
            return theContent;
        } catch (Exception e) {
            throw new RemoteException("Repository failure", e);
        }
    }

    /**
     * Provide the last modified timestamp for a remote file
     */
    public/* synchronized */long lastModified(final String query) {

        final File theFile = nativeLibraryLookup(query);

        if (theFile == null) {
            return 0L;
        }
        return theFile.lastModified();
    }

    /**
     * Lookup the requested file in the repository either for delivery or timestamp checking
     */
    @Nullable
    private File nativeLibraryLookup(final String query) {

        String[] fileList = null;
        int theElement = -1;

        for (int i = 0; i < this.libraryDirectoryFile.size(); i++) {
            final File dir = this.libraryDirectoryFile.get(i);

            if (dir == null) {
                continue;
            }

            fileList = dir.list((dir1, name) -> name.equals(query));

            if (fileList == null || fileList.length == 0) {
                continue;
            }

            theElement = i;
            break;
        }

        if (fileList == null || fileList.length == 0 || theElement == -1) {
            return null;
        }

        return new File(this.libraryDirectoryFile.get(theElement), fileList[0]);
    }

    /**
     * Satisfy the needs of ServiceProviderPlace even though we do not expect any agents to visit here right now.
     */
    @Override
    public void process(final IBaseDataObject theDataObject) {}

    /**
     * Test standalone main
     */

    @SuppressWarnings("SystemOut")
    public static void main(final String[] argv) {

        if (argv.length < 1) {
            System.err.println("usage: java JniRepositoryPlace file.cfg query ...");
            return;
        }

        final JniRepositoryPlace jnirepositoryplace;
        try {
            jnirepositoryplace = new JniRepositoryPlace(argv[0]);
        } catch (IOException e) {
            System.err.println("Cannot make JniRepositoryPlace: " + e);
            return;
        }

        for (int i = 1; i < argv.length; i++) {
            System.err.println(argv[i] + ":" + (jnirepositoryplace.nativeLibraryQuery(argv[i]) ? "" : " not") + " found");
        }
    }
}