Package org.openbel.framework.core.namespace

Source Code of org.openbel.framework.core.namespace.DefaultNamespaceService

/**
* Copyright (C) 2012-2013 Selventa, Inc.
*
* This file is part of the OpenBEL Framework.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The OpenBEL Framework is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms under LGPL v3:
*
* This license does not authorize you and you are prohibited from using the
* name, trademarks, service marks, logos or similar indicia of Selventa, Inc.,
* or, in the discretion of other licensors or authors of the program, the
* name, trademarks, service marks, logos or similar indicia of such authors or
* licensors, in any marketing or advertising materials relating to your
* distribution of the program or any covered product. This restriction does
* not waive or limit your obligation to keep intact all copyright notices set
* forth in the program as delivered to you.
*
* If you distribute the program in whole or in part, or any modified version
* of the program, and you assume contractual liability to the recipient with
* respect to the program or modified version, then you will indemnify the
* authors and licensors of the program for any liabilities that these
* contractual assumptions directly impose on those licensors and authors.
*/
package org.openbel.framework.core.namespace;

import static java.lang.String.format;
import static org.openbel.framework.common.BELUtilities.asPath;
import static org.openbel.framework.common.BELUtilities.nulls;
import static org.openbel.framework.common.PathConstants.NAMESPACE_ROOT_DIRECTORY_NAME;
import static org.openbel.framework.common.Strings.INVALID_SYMBOLS;
import static org.openbel.framework.core.df.cache.ResourceType.NAMESPACES;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import javax.security.auth.DestroyFailedException;
import javax.security.auth.Destroyable;

import org.openbel.framework.common.InvalidArgument;
import org.openbel.framework.common.model.Document;
import org.openbel.framework.common.model.Namespace;
import org.openbel.framework.common.model.Parameter;
import org.openbel.framework.common.model.Statement;
import org.openbel.framework.core.compiler.SymbolWarning;
import org.openbel.framework.core.df.cache.CacheLookupService;
import org.openbel.framework.core.df.cache.CacheableResourceService;
import org.openbel.framework.core.df.cache.CachedResource;
import org.openbel.framework.core.df.cache.ResolvedResource;
import org.openbel.framework.core.indexer.IndexingFailure;
import org.openbel.framework.core.indexer.JDBMNamespaceLookup;
import org.openbel.framework.core.protocol.ResourceDownloadError;

/**
* DefaultNamespaceService implements a service to handle all operations for
* working with namespaces.
* <p>
* This class <strong><em>is thread-safe</em></strong> and allows concurrent
* </p>
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
*/
public final class DefaultNamespaceService implements NamespaceService,
        Destroyable {

    private static final String NS_INDEX_FILE_NAME = "namespace.index";
    private boolean running;

    /**
     * Defines the map of open namespace indexes. The entries are keyed by
     * {@link Namespace} resource location with the {@link JDBMNamespaceLookup} index as
     * the value.
     * <p>
     * This map in thread-safe as it uses {@link ConcurrentHashMap}
     * </p>
     */
    private final Map<String, JDBMNamespaceLookup> openNamespaces =
            new ConcurrentHashMap<String, JDBMNamespaceLookup>();

    /**
     * Defines the map of available namespaces. The entries are the the
     * {@link Namespace} resource locations.
     * <p>
     * This map in thread-safe as it uses {@link ConcurrentHashMap} to create a
     * concurrent hash set.
     * </p>
     */
    private final Set<String> availableNamespaces = Collections
            .newSetFromMap(new ConcurrentHashMap<String, Boolean>());

    /**
     * Defines the resource cache service dependency.
     */
    private final CacheableResourceService resourceCache;

    /**
     * Defines the cache lookup service dependency.
     */
    private final CacheLookupService cacheLookupService;

    /**
     * Defines the namespace indexer service dependency.
     */
    private final NamespaceIndexerService namespaceIndexerService;

    private boolean serviceDestroyed = false;

    public DefaultNamespaceService(
            final CacheableResourceService resourceCache,
            final CacheLookupService cacheLookupService,
            final NamespaceIndexerService namespaceIndexerService) {
        this.resourceCache = resourceCache;
        this.cacheLookupService = cacheLookupService;
        this.namespaceIndexerService = namespaceIndexerService;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isOpen(String resourceLocation) {
        if (resourceLocation == null) {
            throw new InvalidArgument("resourceLocation", resourceLocation);
        }

        return openNamespaces.containsKey(resourceLocation);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void compileNamespace(String resourceLocation)
            throws ResourceDownloadError, IndexingFailure {
        if (resourceLocation == null) {
            throw new InvalidArgument("resourceLocation", resourceLocation);
        }

        synchronized (resourceLocation) {
            // compile
            final String indexPath = doCompile(resourceLocation);

            // close opened namespace index
            closeNamespace(resourceLocation);

            // reopen namespace index
            openNamespace(resourceLocation, indexPath);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void verify(Parameter p) throws NamespaceSyntaxWarning,
            IndexingFailure,
            ResourceDownloadError {
        if (nulls(p, p.getValue())) {
            throw new InvalidArgument("p", p);
        }

        Namespace ns = p.getNamespace();
        if (ns == null) {
            return;
        }

        String resourceLocation = ns.getResourceLocation();
        if (resourceLocation == null) {
            throw new InvalidArgument("resourceLocation", resourceLocation);
        }

        // check and open namespace for this parameter, if it has one
        synchronized (resourceLocation) {
            if (!isOpen(resourceLocation)) {
                final String indexPath = doCompile(resourceLocation);
                openNamespace(resourceLocation, indexPath);
            }
        }

        doVerify(p);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void verify(Statement s) throws SymbolWarning, IndexingFailure,
            ResourceDownloadError {

        List<Parameter> parameters = s.getAllParameters();
        // first iteration to open all namespaces
        for (Parameter p : parameters) {
            if (p.getNamespace() == null
                    || p.getNamespace().getResourceLocation() == null) {
                continue;
            }
            String resourceLocation = p.getNamespace().getResourceLocation();

            synchronized (resourceLocation) {
                if (!isOpen(resourceLocation)) {
                    final String indexPath = doCompile(resourceLocation);
                    openNamespace(resourceLocation, indexPath);
                }
            }
        }

        final List<NamespaceSyntaxWarning> exceptions =
                new ArrayList<NamespaceSyntaxWarning>();
        for (Parameter p : s.getAllParameters()) {
            Namespace ns = p.getNamespace();
            if (ns == null) {
                continue;
            }

            try {
                doVerify(p);
            } catch (NamespaceSyntaxWarning w) {
                exceptions.add(w);
            }
        }

        if (!exceptions.isEmpty()) {
            // TODO review name
            final String name = s.toString();
            final String fmt = INVALID_SYMBOLS;
            final String msg = format(fmt, exceptions.size());
            throw new SymbolWarning(name, msg, exceptions);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void verify(Document d) throws SymbolWarning, IndexingFailure,
            ResourceDownloadError {
        final List<NamespaceSyntaxWarning> exceptions =
                new ArrayList<NamespaceSyntaxWarning>();

        // open document namespaces if necessary
        for (Namespace ns : d.getAllNamespaces()) {
            String resourceLocation = ns.getResourceLocation();

            synchronized (resourceLocation) {
                if (!isOpen(resourceLocation)) {
                    final String indexPath = doCompile(resourceLocation);
                    openNamespace(resourceLocation, indexPath);
                }
            }
        }

        for (final Parameter p : d.getAllParameters()) {
            Namespace ns = p.getNamespace();
            if (ns == null) {
                continue;
            }

            try {
                doVerify(p);
            } catch (NamespaceSyntaxWarning e) {
                exceptions.add(e);
            }
        }

        if (!exceptions.isEmpty()) {
            final String name = d.getName();
            final String fmt = INVALID_SYMBOLS;
            final String msg = format(fmt, exceptions.size());
            throw new SymbolWarning(name, msg, exceptions);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String lookup(Parameter p) throws NamespaceSyntaxWarning,
            IndexingFailure {
        if (nulls(p, p.getValue())) {
            throw new InvalidArgument("parameter is null");
        }

        Namespace ns = p.getNamespace();
        if (ns == null) {
            return null;
        }

        String resourceLocation = ns.getResourceLocation();
        if (resourceLocation == null) {
            throw new InvalidArgument("resourceLocation", resourceLocation);
        }

        // get opened namespace and lookup namespace parameter encoding
        JDBMNamespaceLookup il = openNamespaces.get(ns.getResourceLocation());
        if (il == null) {
            final String fmt = "Namespace '%s' is not open.";
            final String msg = String.format(fmt, resourceLocation);
            throw new IndexingFailure(resourceLocation, msg);
        }

        String encoding = il.lookup(p.getValue());

        if (encoding == null) {
            String rl = ns.getResourceLocation();
            String pref = ns.getPrefix();
            throw new NamespaceSyntaxWarning(rl, pref, p.getValue());
        }
        return encoding;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<String> search(String resourceLocation, Pattern pattern)
            throws IndexingFailure, ResourceDownloadError {
        if (resourceLocation == null) {
            throw new InvalidArgument("resourceLocation", resourceLocation);
        }
        if (pattern == null) {
            throw new InvalidArgument("pattern", pattern);
        }

        // check and open namespace for this resource location
        synchronized (resourceLocation) {
            if (!isOpen(resourceLocation)) {
                String indexPath = doCompile(resourceLocation);
                openNamespace(resourceLocation, indexPath);
            }
        }

        return doSearch(resourceLocation, pattern);
    }

    /**
     * Do compilation of the namespace resource.
     *
     * @param resourceLocation {@link String}, the resource location to compile
     * @throws IndexingFailure Thrown if an error occurred while indexing
     * @throws ResourceDownloadError Thrown if an error occurred downloading
     * resource
     */
    private String doCompile(String resourceLocation) throws IndexingFailure,
            ResourceDownloadError {
        final ResolvedResource resolved =
                resourceCache.resolveResource(NAMESPACES, resourceLocation);
        final File resourceCopy = resolved.getCacheResourceCopy();
        namespaceIndexerService.indexNamespace(
                resourceLocation, resolved.getCacheResourceCopy());

        final String indexPath = asPath(resourceCopy.getParent(),
                NAMESPACE_ROOT_DIRECTORY_NAME, NS_INDEX_FILE_NAME);
        return indexPath;
    }

    /**
     * Do a search on the values in namespace specified by the resource location
     *
     * @param resourceLocation resource location, e.g.,
     * "http://resource.belframework.org/belframework/1.0/ns/chebi-ids.belns" ,
     * can not be null
     * @param pattern {@link Pattern}, can not be null
     * @return {@link Set} of {@link String}s containing values that match
     */
    private Set<String> doSearch(String resourceLocation, Pattern pattern) {
        // get opened namespace
        JDBMNamespaceLookup il = openNamespaces.get(resourceLocation);

        Set<String> results = new HashSet<String>();
        for (String value : il.getKeySet()) {
            if (pattern.matcher(value).matches()) {
                results.add(value);
            }
        }

        return results;
    }

    /**
     * Do namespace value verification against a resource location. This
     * implementation assumes the namespace has been open prior to execution.
     *
     * @param p {@link Parameter}, the parameter to verify namespace value for
     * which cannot be null and must have a non-null namespace and value
     * @throws NamespaceSyntaxWarning Thrown if parameter's {@link Namespace} is
     * not null and it does not contain the parameter's value
     * @throws InvalidArgument Thrown if <tt>p</tt> argument is null, its value
     * is null, or if its namespace's resource location is null
     */
    private void doVerify(Parameter p) throws NamespaceSyntaxWarning {
        if (p.getValue() == null) {
            throw new InvalidArgument("parameter value is null");
        }

        Namespace ns = p.getNamespace();

        String resourceLocation = ns.getResourceLocation();
        if (resourceLocation == null) {
            throw new InvalidArgument("resourceLocation", resourceLocation);
        }

        // get opened namespace and lookup namespace parameter encoding
        JDBMNamespaceLookup il = openNamespaces.get(ns.getResourceLocation());
        if (il == null) {
            throw new IllegalStateException("namespace index is not open.");
        }

        String encoding = il.lookup(p.getValue());
        if (encoding == null) {
            throw new NamespaceSyntaxWarning(ns.getResourceLocation(),
                    ns.getPrefix(),
                    p.getValue());
        }
    }

    /**
     * Determines if the {@link Namespace} resource location can be opened for
     * lookups. To be opened
     *
     * @param resourceLocation
     * @return
     */
    private boolean canOpen(String resourceLocation) {
        if (resourceLocation == null) {
            throw new InvalidArgument("resourceLocation", resourceLocation);
        }

        synchronized (resourceLocation) {
            // find the resource in the cache
            CachedResource cachedResource = cacheLookupService
                    .findInCache(NAMESPACES, resourceLocation);

            if (cachedResource == null) {
                return false;
            }

            String indexPath = asPath(
                    cachedResource.getLocalFile().getAbsolutePath(),
                    NS_INDEX_FILE_NAME);

            JDBMNamespaceLookup il = new JDBMNamespaceLookup(indexPath);
            try {
                il.open();
                il.close();
            } catch (IOException e) {
                return false;
            }

            return true;
        }
    }

    private void openNamespace(final String resourceLocation,
            final String indexPath) throws IndexingFailure {
        if (resourceLocation == null) {
            throw new InvalidArgument("resourceLocation", resourceLocation);
        }

        if (openNamespaces.containsKey(resourceLocation)) {
            throw new IllegalStateException("namespace " + resourceLocation
                    + " is already open.");
        }

        synchronized (resourceLocation) {
            JDBMNamespaceLookup il = new JDBMNamespaceLookup(indexPath);
            try {
                il.open();
            } catch (IOException e) {
                final String fmt = "Failed to open namespace '%s'.";
                final String msg = String.format(fmt, resourceLocation);
                throw new IndexingFailure(resourceLocation, msg, e);
            }

            openNamespaces.put(resourceLocation, il);
        }
    }

    private void closeNamespace(final String resourceLocation)
            throws IndexingFailure {
        synchronized (resourceLocation) {
            JDBMNamespaceLookup jdbmlookup =
                    openNamespaces.get(resourceLocation);
            if (jdbmlookup != null) {
                try {
                    jdbmlookup.close();
                    openNamespaces.remove(resourceLocation);
                } catch (IOException e) {
                    final String fmt = "Failed to close namespace '%s'.";
                    final String msg = String.format(fmt, resourceLocation);
                    throw new IndexingFailure(resourceLocation, msg, e);
                }
            }
        }
    }

    private void closeNamespaces() throws IndexingFailure {
        // close each JDBM btree index in turn
        for (Map.Entry<String, JDBMNamespaceLookup> entry : openNamespaces
                .entrySet()) {
            closeNamespace(entry.getKey());
        }

        // clear out the open namespaces map
        openNamespaces.clear();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void destroy() throws DestroyFailedException {
        try {
            closeNamespaces();
            serviceDestroyed = true;
        } catch (IndexingFailure e) {
            throw new DestroyFailedException(
                    "Namespace service cannot be destroyed.");
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isDestroyed() {
        return serviceDestroyed;
    }

    public boolean isRunning() {
        return running;
    }

    public void start() {
        List<CachedResource> resources = cacheLookupService.getResources();

        for (CachedResource resource : resources) {
            if (NAMESPACES.equals(resource.getType())) {
                String resourceLocation = resource.getRemoteLocation();

                if (canOpen(resourceLocation)) {
                    availableNamespaces.add(resourceLocation);
                }
            }
        }
        running = true;
    }

    public void stop() {
    }
}
TOP

Related Classes of org.openbel.framework.core.namespace.DefaultNamespaceService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.