org.polymap.p4.catalog.AllResolver.java Source code

Java tutorial

Introduction

Here is the source code for org.polymap.p4.catalog.AllResolver.java

Source

/* 
 * polymap.org
 * Copyright (C) 2015-2016, Falko Brutigam. All rights reserved.
 *
 * This 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.0 of
 * the License, or (at your option) any later version.
 *
 * This software 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.
 */
package org.polymap.p4.catalog;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

import org.geotools.data.FeatureSource;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.common.base.Throwables;

import org.eclipse.core.runtime.IProgressMonitor;

import org.polymap.core.catalog.IMetadata;
import org.polymap.core.catalog.IMetadataCatalog;
import org.polymap.core.catalog.resolve.IMetadataResourceResolver;
import org.polymap.core.catalog.resolve.IResolvableInfo;
import org.polymap.core.catalog.resolve.IResourceInfo;
import org.polymap.core.catalog.resolve.IServiceInfo;
import org.polymap.core.catalog.resolve.ResourceResolverExtension;
import org.polymap.core.data.pipeline.DataSourceDescription;
import org.polymap.core.project.ILayer;
import org.polymap.core.runtime.JobExecutor;
import org.polymap.core.runtime.UIJob;
import org.polymap.core.runtime.cache.Cache;
import org.polymap.core.runtime.cache.CacheConfig;

import org.polymap.p4.P4Plugin;
import org.polymap.p4.layer.FeatureLayer;

/**
 * Provides the connection between an {@link ILayer} -> {@link IMetadata} ->
 * {@link IServiceInfo} and back.
 * <p/>
 * Holds a list of delegate {@link IMetadataResourceResolver}s which are responsible
 * of actually creating a service/resource out of a metadata entry. A {@link ILayer}
 * is connected to a metadata entry via the
 * {@link #resourceIdentifier(IResourceInfo)} which consists of the metadata
 * identifier and the resource name.
 *
 * @see IMetadataResourceResolver
 * @author <a href="http://www.polymap.de">Falko Brutigam</a>
 */
public class AllResolver implements IMetadataResourceResolver {

    private static Log log = LogFactory.getLog(AllResolver.class);

    public static final char ID_DELIMITER = '|';

    /**
     * Returns {@link P4Plugin#allResolver()}.
     */
    public static AllResolver instance() {
        return P4Plugin.allResolver();
    }

    // instance *******************************************

    private List<IMetadataCatalog> catalogs;

    /** The delegates. */
    private List<IMetadataResourceResolver> resolvers;

    /**
     * Caches {@link IResolvableInfo} instances in order to have just one underlying
     * service instances (WMS, Shape, RDataStore, etc.) per JVM.
     * <p/>
     * Using {@link ConcurrentHashMap} instead of {@link Cache} ensures that mapping
     * function is executed at most once per key and there is actually just one
     * resolvable constructed per metadata.
     */
    private Map<IMetadata, CompletableFuture<IResolvableInfo>> resolved = new ConcurrentHashMap();

    /**
     * Caches {@link IMetadata}.
     */
    private Cache<String, IMetadata> metadataCache = CacheConfig.defaults().initSize(128).createCache();

    public AllResolver(List<IMetadataCatalog> catalogs) {
        assert !catalogs.isEmpty();
        this.catalogs = catalogs;
        this.resolvers = ResourceResolverExtension.createAllResolvers();
    }

    public static String resourceIdentifier(IResourceInfo res) {
        IServiceInfo serviceInfo = res.getServiceInfo();
        IMetadata metadata = serviceInfo.getMetadata();
        return metadata.getIdentifier() + ID_DELIMITER + res.getName();
    }

    /**
     * Index 0: metadata identifier; index 1: resource name 
     */
    public static String[] parseResourceIdentifier(String resId) {
        assert resId.indexOf(ID_DELIMITER) > 0;
        return StringUtils.split(resId, ID_DELIMITER);
    }

    /**
     * Connects the given layer with its backend service.
     * <p/>
     * Consider {@link FeatureLayer} if interested in the {@link FeatureSource}
     * of a layer.
     *
     * @see FeatureLayer
     * @param layer
     * @param monitor
     * @return Newly created {@link DataSourceDescription} with (cached) service
     *         instance.
     * @throws Exception
     */
    public Optional<DataSourceDescription> connectLayer(ILayer layer, IProgressMonitor monitor) throws Exception {
        IServiceInfo serviceInfo = serviceInfo(layer, monitor).orElse(null);

        if (serviceInfo != null) {
            String resName = parseResourceIdentifier(layer.resourceIdentifier.get())[1];
            Object service = serviceInfo.createService(monitor);

            return Optional.of(new DataSourceDescription().service.put(service).resourceName.put(resName));
        }
        return Optional.empty();
    }

    public Optional<IMetadata> metadata(ILayer layer, IProgressMonitor monitor) throws Exception {
        String metadataId = parseResourceIdentifier(layer.resourceIdentifier.get())[0];

        return Optional.ofNullable(metadataCache.get(metadataId, key -> {
            for (IMetadataCatalog catalog : catalogs) {
                Optional<? extends IMetadata> result = catalog.entry(metadataId, monitor);
                if (result.isPresent()) {
                    return result.get();
                }
            }
            return null;
        }));
    }

    public Optional<IServiceInfo> serviceInfo(ILayer layer, IProgressMonitor monitor) throws Exception {
        IMetadata metadata = metadata(layer, monitor).orElse(null);

        return Optional.ofNullable(metadata != null ? (IServiceInfo) resolve(metadata, monitor) : null);
    }

    public Optional<IResourceInfo> resInfo(ILayer layer, IProgressMonitor monitor) throws Exception {
        IServiceInfo serviceInfo = serviceInfo(layer, monitor).orElse(null);

        if (serviceInfo != null) {
            String resName = parseResourceIdentifier(layer.resourceIdentifier.get())[1];
            for (IResourceInfo info : serviceInfo.getResources(monitor)) {
                if (info.getName().equals(resName)) {
                    return Optional.of(info);
                }
            }
        }
        return Optional.empty();
    }

    // IMetadataResourceResolver **************************

    /**
     * {@inheritDoc}
     * <p/>
     * Resolves info for the given metadata and caches the result.
     * 
     * @param metadata
     * @param monitor
     * @return Created or cached info instance.
     */
    @Override
    public CompletableFuture<IResolvableInfo> resolve(IMetadata metadata) {
        return resolved.computeIfAbsent(metadata, key -> {
            return doResolve(metadata, null);
        });
    }

    @Override
    public IResolvableInfo resolve(IMetadata metadata, IProgressMonitor monitor) throws Exception {
        return resolved.computeIfAbsent(metadata, key -> {
            return doResolve(metadata, monitor);
        }).get();
    }

    protected CompletableFuture<IResolvableInfo> doResolve(IMetadata metadata, IProgressMonitor monitor) {
        for (IMetadataResourceResolver resolver : resolvers) {
            if (resolver.canResolve(metadata)) {
                return CompletableFuture.supplyAsync(() -> {
                    try {
                        IProgressMonitor mon = monitor != null ? monitor : UIJob.monitorOfThread();
                        return resolver.resolve(metadata, mon);
                    } catch (Exception e) {
                        log.warn("", e);
                        throw Throwables.propagate(e);
                    }
                }, JobExecutor.instance());
            }
        }
        throw new IllegalStateException("Unable to resolve: " + metadata);
    }

    @Override
    public boolean canResolve(IMetadata metadata) {
        return resolved.get(metadata) == null
                ? resolvers.stream().filter(r -> r.canResolve(metadata)).findFirst().isPresent()
                : true;
    }

    @Override
    public Map<String, String> createParams(Object service) {
        throw new RuntimeException("Not yet implemented for AllResolver.");
    }

}