org.commonjava.cartographer.graph.spi.neo4j.FileNeo4jConnectionFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.commonjava.cartographer.graph.spi.neo4j.FileNeo4jConnectionFactory.java

Source

/**
 * Copyright (C) 2012 Red Hat, Inc. (jdcasey@commonjava.org)
 *
 * Licensed 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.commonjava.cartographer.graph.spi.neo4j;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.commonjava.cartographer.graph.spi.RelationshipGraphConnection;
import org.commonjava.cartographer.graph.spi.RelationshipGraphConnectionException;
import org.commonjava.cartographer.graph.spi.RelationshipGraphConnectionFactory;
import org.neo4j.kernel.StoreLockException;
import org.neo4j.kernel.lifecycle.LifecycleException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.channels.OverlappingFileLockException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class FileNeo4jConnectionFactory implements RelationshipGraphConnectionFactory {
    private final Map<String, FileNeo4JGraphConnection> openConnections = new HashMap<String, FileNeo4JGraphConnection>();

    private final File dbBaseDirectory;

    private final boolean useShutdownHook;

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private int storageBatchSize = FileNeo4JGraphConnection.DEFAULT_BATCH_SIZE;

    public FileNeo4jConnectionFactory(final File dbBaseDirectory, final boolean useShutdownHook,
            final int storageBatchSize) {
        this.dbBaseDirectory = dbBaseDirectory;
        this.useShutdownHook = useShutdownHook;
        this.storageBatchSize = storageBatchSize;
    }

    public FileNeo4jConnectionFactory(final File dbBaseDirectory, final boolean useShutdownHook) {
        this.dbBaseDirectory = dbBaseDirectory;
        this.useShutdownHook = useShutdownHook;
    }

    @Override
    public synchronized RelationshipGraphConnection openConnection(final String workspaceId, final boolean create)
            throws RelationshipGraphConnectionException {
        final File db = new File(dbBaseDirectory, workspaceId);
        if (!db.exists()) {
            if (!create) {
                throw new RelationshipGraphConnectionException("Workspace does not exist: %s.", workspaceId);
            } else if (!db.mkdirs()) {
                throw new RelationshipGraphConnectionException(
                        "Failed to create workspace directory for: %s. (dir: %s)", workspaceId, db);
            }
            //
            //            try
            //            {
            //                Thread.sleep( 20 );
            //            }
            //            catch ( final InterruptedException e )
            //            {
            //                Thread.currentThread()
            //                      .interrupt();
            //                return null;
            //            }
        }

        FileNeo4JGraphConnection conn = openConnections.get(workspaceId);
        if (conn == null || !conn.isOpen()) {
            conn = null;
            int attempt = 0;

            while (conn == null) {
                attempt++;
                try {
                    conn = new FileNeo4JGraphConnection(workspaceId, db, useShutdownHook, storageBatchSize, this);
                } catch (RuntimeException ex) {
                    if (ex.getCause() instanceof LifecycleException
                            && ex.getCause().getCause() instanceof StoreLockException
                            && ex.getCause().getCause().getCause() instanceof OverlappingFileLockException
                            && attempt < 3) {
                        logger.warn("Tried to connect to DB which is not closed (yet). {} Retrying in 5s.",
                                ex.toString());
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException ez) {
                            logger.error("The wait delay was interrupted.", ex);
                        }
                    } else {
                        throw ex;
                    }
                }
            }

            openConnections.put(workspaceId, conn);
        }

        return conn;
    }

    @Override
    public Set<String> listWorkspaces() {
        if (!dbBaseDirectory.exists()) {
            return Collections.emptySet();
        }

        String[] listing = dbBaseDirectory.list();
        if (listing == null) {
            return Collections.emptySet();
        }

        return new HashSet<String>(Arrays.asList(listing));
    }

    @Override
    public void flush(final RelationshipGraphConnection connection) throws RelationshipGraphConnectionException {
        // TODO How do I flush the graph to disk while other views may be modifying it??
    }

    @Override
    public boolean delete(final String workspaceId) throws RelationshipGraphConnectionException {
        final File db = new File(dbBaseDirectory, workspaceId);
        if (!db.exists() || !db.isDirectory()) {
            return false;
        }

        try {
            final FileNeo4JGraphConnection connection = openConnections.remove(workspaceId);
            if (connection != null) {
                connection.close();
            }

            FileUtils.forceDelete(db);
            return !db.exists();
        } catch (final IOException e) {
            throw new RelationshipGraphConnectionException("Failed to delete: %s. Reason: %s", e, db,
                    e.getMessage());
        }
    }

    @Override
    public synchronized void close() throws IOException {
        final Logger logger = LoggerFactory.getLogger(getClass());

        final Set<String> failedClose = new HashSet<String>();
        for (final FileNeo4JGraphConnection conn : new HashSet<FileNeo4JGraphConnection>(
                openConnections.values())) {
            try {
                conn.close();
            } catch (final IOException e) {
                failedClose.add(conn.getWorkspaceId());
                logger.error("Failed to close: " + conn.getWorkspaceId() + ".", e);
            }
        }

        openConnections.clear();

        if (!failedClose.isEmpty()) {
            throw new IOException("Failed to close: " + StringUtils.join(failedClose, ", "));
        }
    }

    @Override
    public boolean exists(final String workspaceId) {
        final File db = new File(dbBaseDirectory, workspaceId);
        return db.exists() && db.isDirectory();
    }

    public synchronized void connectionClosing(final String workspaceId) {
        openConnections.remove(workspaceId);
    }

}