org.polymap.core.data.pipeline.DefaultPipelineIncubator.java Source code

Java tutorial

Introduction

Here is the source code for org.polymap.core.data.pipeline.DefaultPipelineIncubator.java

Source

/*
 * polymap.org
 * Copyright 2009, Polymap GmbH, and individual contributors as indicated
 * by the @authors tag.
 *
 * 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 2.1 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 *
 * $Id$
 */
package org.polymap.core.data.pipeline;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import net.refractions.udig.catalog.IService;

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

import org.eclipse.core.runtime.CoreException;

import org.polymap.core.data.DataPlugin;
import org.polymap.core.data.feature.AddFeaturesRequest;
import org.polymap.core.data.feature.DataSourceProcessor;
import org.polymap.core.data.feature.FeatureRenderProcessor2;
import org.polymap.core.data.feature.GetFeatureTypeRequest;
import org.polymap.core.data.feature.GetFeatureTypeResponse;
import org.polymap.core.data.feature.GetFeaturesRequest;
import org.polymap.core.data.feature.GetFeaturesResponse;
import org.polymap.core.data.feature.GetFeaturesSizeRequest;
import org.polymap.core.data.feature.GetFeaturesSizeResponse;
import org.polymap.core.data.feature.ModifyFeaturesRequest;
import org.polymap.core.data.feature.ModifyFeaturesResponse;
import org.polymap.core.data.feature.RemoveFeaturesRequest;
import org.polymap.core.data.image.EncodedImageResponse;
import org.polymap.core.data.image.GetLayerTypesRequest;
import org.polymap.core.data.image.GetLayerTypesResponse;
import org.polymap.core.data.image.GetLegendGraphicRequest;
import org.polymap.core.data.image.GetMapRequest;
import org.polymap.core.data.image.ImageDecodeProcessor;
import org.polymap.core.data.image.ImageEncodeProcessor;
import org.polymap.core.data.image.ImageResponse;
import org.polymap.core.data.image.RasterRenderProcessor;
import org.polymap.core.data.image.WmsRenderProcessor;
import org.polymap.core.project.ILayer;
import org.polymap.core.project.IMap;
import org.polymap.core.project.LayerUseCase;
import org.polymap.core.project.PipelineProcessorConfiguration;
import org.polymap.core.runtime.ListenerList;
import org.polymap.core.runtime.SessionSingleton;
import org.polymap.core.workbench.PolymapWorkbench;

/**
 *
 *
 * @author <a href="http://www.polymap.de">Falko Braeutigam</a>
 * @version POLYMAP3 ($Revision$)
 * @since 3.0
 */
public class DefaultPipelineIncubator implements IPipelineIncubator {

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

    private static List<Class<? extends PipelineProcessor>> transformers = new ArrayList();

    private static List<Class<? extends ITerminalPipelineProcessor>> terminals = new ArrayList();

    static {
        // XXX get transfomers/terminals from extensions
        synchronized (transformers) {
            log.info("Initializing standard transformers...");
            transformers.add(ImageEncodeProcessor.class);
            transformers.add(ImageDecodeProcessor.class);
        }
        synchronized (terminals) {
            log.info("Initializing standard terminal processors...");
            terminals.add(WmsRenderProcessor.class);
            terminals.add(FeatureRenderProcessor2.class);
            terminals.add(DataSourceProcessor.class);
            terminals.add(RasterRenderProcessor.class);

            for (ProcessorExtension ext : ProcessorExtension.allExtensions()) {
                if (ext.isTerminal()) {
                    try {
                        log.debug("    Terminal processor type found: " + ext.getId());
                        terminals.add((Class<? extends ITerminalPipelineProcessor>) ext.newProcessor().getClass());
                    } catch (Exception e) {
                        log.warn(e.getMessage(), e);
                    }
                }
            }
        }
    }

    //    /**
    //     * Add a {@link IPageChangedListener} to the global list of listeners of this
    //     * session.
    //     */
    //    public static void addIncubationListener( IPipelineIncubationListener l ) {
    //        Session.instance().listeners.add( l );
    //    }
    //    
    //    public static void removeIncubationListener( IPipelineIncubationListener l ) {
    //        Session.instance().listeners.remove( l );
    //    }

    /**
     * Session bound incubation listeners. 
     */
    static class Session extends SessionSingleton {

        private ListenerList<IPipelineIncubationListener> listeners = new ListenerList();

        public static Session instance() {
            return instance(Session.class);
        }

        protected Session() {
            for (PipelineListenerExtension ext : PipelineListenerExtension.allExtensions()) {
                try {
                    IPipelineIncubationListener listener = ext.newListener();
                    listeners.add(listener);
                } catch (CoreException e) {
                    log.error("Unable to create a new IPipelineIncubationListener: " + ext.getId());
                }
            }
        }

    }

    public Pipeline newPipeline(LayerUseCase usecase, IMap map, ILayer layer, IService service)
            throws PipelineIncubationException {
        log.debug("New pipeline for service: " + service);
        //        IGeoResource geores = layer.getGeoResource();

        // terminal
        List<ProcessorDescription> terms = findTerminals(service, usecase);
        if (terms.isEmpty()) {
            throw new PipelineIncubationException("No terminal for service: " + service.getClass().getName());
        }

        // transformer chain
        LinkedList<ProcessorDescription> chain = null;
        for (ProcessorDescription term : terms) {
            chain = new LinkedList();
            ProcessorDescription start = new ProcessorDescription(signatureForUsecase(usecase));

            if (findTransformation(start, term, usecase, chain)) {
                break;
            } else {
                log.debug("No transformer chain for terminal: " + term);
            }
        }
        if (chain == null) {
            throw new PipelineIncubationException(
                    "No transformer chain for: layer=" + layer + ", usecase=" + usecase);
        }

        // add layer specific processors
        if (layer != null) {
            PipelineProcessorConfiguration[] procConfigs = layer.getProcessorConfigs();
            for (PipelineProcessorConfiguration procConfig : procConfigs) {
                ProcessorExtension ext = ProcessorExtension.forExtensionId(procConfig.getExtensionId());

                if (ext == null) {
                    log.warn("No processor extension found for: " + procConfig.getExtensionId() + "!!!");
                    break;
                }
                try {
                    PipelineProcessor processor = ext.newProcessor();
                    ProcessorDescription candidate = new ProcessorDescription(processor.getClass(),
                            procConfig.getConfig(), usecase);
                    int i = 0;
                    for (ProcessorDescription chainElm : chain) {
                        if (candidate.getSignature().isCompatible(chainElm.getSignature())) {
                            log.debug("      Insert configured processor: " + candidate.toString() + " at: " + i);
                            chain.add(i, candidate);
                            break;
                        }
                        i++;
                    }
                } catch (CoreException e) {
                    log.warn(e);
                    PolymapWorkbench.handleError(DataPlugin.PLUGIN_ID, this, e.getLocalizedMessage(), e);

                }
            }
        }

        // create the pipeline
        Pipeline pipeline = new Pipeline(map, layer, service);
        for (ProcessorDescription desc : chain) {
            PipelineProcessor processor = desc.newProcessor();
            Properties props = desc.getProps() != null ? desc.getProps() : new Properties();
            if (layer != null) {
                props.put("layer", layer);
            }
            if (map != null) {
                props.put("map", map);
            }
            props.put("service", service);
            processor.init(props);
            pipeline.addLast(processor);
        }

        // call listeners
        for (IPipelineIncubationListener listener : Session.instance().listeners) {
            listener.pipelineCreated(pipeline);
        }

        return pipeline;
    }

    protected List<ProcessorDescription> findTerminals(IService service, LayerUseCase usecase) {
        List<ProcessorDescription> result = new ArrayList();
        for (ProcessorDescription desc : allTerminals(usecase)) {
            if (desc.isCompatible(service)) {
                log.debug("Terminal for '" + service + "' -- " + usecase + " : " + desc);
                result.add(desc);
            }
        }
        return result;
    }

    protected List<ProcessorDescription> allTerminals(LayerUseCase usecase) {
        List<ProcessorDescription> result = new ArrayList(terminals.size());
        for (Class<? extends PipelineProcessor> cl : terminals) {
            result.add(new ProcessorDescription(cl, null, usecase));
        }
        return result;
    }

    protected boolean findTransformation(ProcessorDescription from, ProcessorDescription to, LayerUseCase usecase,
            Deque<ProcessorDescription> chain) {
        log.debug(StringUtils.repeat("    ", chain.size()) + "findTransformation: " + from + " => " + to + " -- "
                + usecase);

        // recursion break
        if (chain.size() > 16) {
            return false;
        }

        // recursion start
        if (from.getSignature().isCompatible(to.getSignature())) {
            chain.addLast(to);
            log.debug(StringUtils.repeat("    ", chain.size()) + "Transformation found: " + chain);
            return true;
        }

        // recursion step
        else {
            for (ProcessorDescription desc : allTransformers(usecase)) {
                if (from.getSignature().isCompatible(desc.getSignature()) && !chain.contains(desc)) {
                    chain.addLast(desc);
                    if (findTransformation(desc, to, usecase, chain)) {
                        //log.debug( "      transformation found: " + desc );
                        return true;
                    }
                    chain.removeLast();
                }
            }
            return false;
        }
    }

    protected List<ProcessorDescription> allTransformers(LayerUseCase usecase) {
        List<ProcessorDescription> result = new ArrayList(transformers.size());
        for (Class<? extends PipelineProcessor> cl : transformers) {
            result.add(new ProcessorDescription(cl, null, usecase));
        }
        return result;
    }

    /**
     * Create a new signature for the given usecase.
     * <p>
     * XXX Use case and its signature are closely related. It is not a that good
     * idea to have both separated. However, currently the {@link LayerUseCase}
     * is part of the project bundle, which does not know about pipelines.
     *
     * @param usecase
     * @return Newly created signature for the given usecase.
     * @throws RuntimeException If no signature is found.
     */
    public static ProcessorSignature signatureForUsecase(LayerUseCase usecase) {
        // WMS request/response signature
        if (usecase.isCompatible(LayerUseCase.ENCODED_IMAGE)) {
            return new ProcessorSignature(new Class[] {},
                    new Class[] { GetMapRequest.class, GetLegendGraphicRequest.class, GetLayerTypesRequest.class },
                    new Class[] { EncodedImageResponse.class, GetLayerTypesResponse.class }, new Class[] {});
        }
        // upstream WMS service
        else if (usecase.isCompatible(LayerUseCase.IMAGE)) {
            return new ProcessorSignature(new Class[] {},
                    new Class[] { GetMapRequest.class, GetLegendGraphicRequest.class, GetLayerTypesRequest.class },
                    new Class[] { ImageResponse.class, GetLayerTypesResponse.class }, new Class[] {});
        }
        // WFS-T request/response signature (before WFS)
        else if (usecase.isCompatible(LayerUseCase.FEATURES_TRANSACTIONAL)) {
            return new ProcessorSignature(new Class[] {},
                    new Class[] { ModifyFeaturesRequest.class, RemoveFeaturesRequest.class,
                            AddFeaturesRequest.class, GetFeatureTypeRequest.class, GetFeaturesRequest.class,
                            GetFeaturesSizeRequest.class },
                    new Class[] { ModifyFeaturesResponse.class, GetFeatureTypeResponse.class,
                            GetFeaturesResponse.class, GetFeaturesSizeResponse.class },
                    new Class[] {});
        }
        // WFS request/response signature
        else if (usecase.isCompatible(LayerUseCase.FEATURES)) {
            return new ProcessorSignature(new Class[] {},
                    new Class[] { GetFeatureTypeRequest.class, GetFeaturesRequest.class,
                            GetFeaturesSizeRequest.class },
                    new Class[] { GetFeatureTypeResponse.class, GetFeaturesResponse.class,
                            GetFeaturesSizeResponse.class },
                    new Class[] {});
        } else {
            throw new RuntimeException("No signature specified yet for usecase: " + usecase);
        }
    }

}