org.geowebcache.layer.TileLayerDispatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.geowebcache.layer.TileLayerDispatcher.java

Source

/**
 * 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.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @author Arne Kepp, The Open Planning Project, Copyright 2008
 */
package org.geowebcache.layer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geowebcache.GeoWebCacheException;
import org.geowebcache.GeoWebCacheExtensions;
import org.geowebcache.config.Configuration;
import org.geowebcache.config.XMLConfiguration;
import org.geowebcache.config.XMLGridSet;
import org.geowebcache.config.meta.ServiceInformation;
import org.geowebcache.grid.GridSet;
import org.geowebcache.grid.GridSetBroker;
import org.geowebcache.grid.GridSubset;
import org.geowebcache.util.CompositeIterable;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.util.Assert;

import com.google.common.base.Preconditions;

/**
 * Serves tile layers from the {@link Configuration}s available in the application context.
 */
public class TileLayerDispatcher implements DisposableBean {

    private static Log log = LogFactory.getLog(org.geowebcache.layer.TileLayerDispatcher.class);

    private List<Configuration> configs;

    private GridSetBroker gridSetBroker;

    private ServiceInformation serviceInformation;

    /**
     * @deprecated use {@link #TileLayerDispatcher(GridSetBroker)} instead, configurations are
     *             loaded from the application context, this {@code config} parameter will be
     *             ignored
     */
    public TileLayerDispatcher(GridSetBroker gridSetBroker, List<Configuration> configs) {
        this.gridSetBroker = gridSetBroker;
        this.configs = configs == null ? new ArrayList<Configuration>() : configs;
        initialize();
    }

    public TileLayerDispatcher(GridSetBroker gridSetBroker) {
        this.gridSetBroker = gridSetBroker;
        reInit();
    }

    public void addConfiguration(Configuration config) {
        initialize(config);
        List<Configuration> newList = new ArrayList<Configuration>(configs);
        newList.add(config);
        this.configs = newList;
    }

    public boolean layerExists(final String layerName) {
        for (int i = 0; i < configs.size(); i++) {
            Configuration configuration = configs.get(i);
            TileLayer layer = configuration.getTileLayer(layerName);
            if (layer != null) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the layer named after the {@code layerName} parameter.
     * 
     * @throws GeoWebCacheException
     *             if no such layer exists
     */
    public TileLayer getTileLayer(final String layerName) throws GeoWebCacheException {
        Preconditions.checkNotNull(layerName, "layerName is null");

        for (int i = 0; i < configs.size(); i++) {
            Configuration configuration = configs.get(i);
            TileLayer layer = configuration.getTileLayer(layerName);
            if (layer != null) {
                return layer;
            }
        }
        throw new GeoWebCacheException("Thread " + Thread.currentThread().getId() + " Unknown layer " + layerName
                + ". Check the logfiles," + " it may not have loaded properly.");
    }

    /***
     * Reinitialization is tricky, because we can't really just lock all the layers, because this
     * would cause people to queue on something that we may not want to exist post reinit.
     * 
     * So we'll just set the current layer set free, ready for garbage collection, and generate a
     * new one.
     * 
     */
    public void reInit() {
        List<Configuration> extensions = GeoWebCacheExtensions.extensions(Configuration.class);
        this.configs = new ArrayList<Configuration>(extensions);
        initialize();
    }

    public int getLayerCount() {
        int count = 0;
        for (int i = 0; i < configs.size(); i++) {
            Configuration configuration = configs.get(i);
            count += configuration.getTileLayerCount();
        }
        return count;
    }

    public Set<String> getLayerNames() {
        Set<String> names = new HashSet<String>();
        for (int i = 0; i < configs.size(); i++) {
            Configuration configuration = configs.get(i);
            names.addAll(configuration.getTileLayerNames());
        }
        return names;
    }

    /**
     * Returns a list of all the layers. The consumer may still have to initialize each layer!
     * <p>
     * Modifications to the returned layer do not change the internal list of layers, but layers ARE
     * mutable.
     * </p>
     * 
     * @return a list view of this tile layer dispatcher's internal layers
     */
    @SuppressWarnings("unchecked")
    public Iterable<TileLayer> getLayerList() {
        List<Iterable<TileLayer>> perConfigLayers = new ArrayList<Iterable<TileLayer>>(configs.size());

        for (Configuration config : configs) {
            perConfigLayers.add((Iterable<TileLayer>) config.getLayers());
        }

        return new CompositeIterable<TileLayer>(perConfigLayers);
    }

    private void initialize() {
        log.debug("Thread initLayers(), initializing");

        for (Configuration config : configs) {
            initialize(config);
        }
    }

    private int initialize(Configuration config) {
        if (config == null) {
            throw new IllegalStateException("TileLayerDispatcher got a null GWC configuration object");
        }

        String configIdent = null;
        try {
            configIdent = config.getIdentifier();
        } catch (Exception gwce) {
            log.error("Error obtaining identify from Configuration " + config, gwce);
            return 0;
        }

        if (configIdent == null) {
            log.warn("Got a GWC configuration with no identity, ignoring it:" + config);
            return 0;
        }

        int layerCount;
        try {
            layerCount = config.initialize(gridSetBroker);
        } catch (GeoWebCacheException gwce) {
            log.error("Failed to add layers from " + configIdent, gwce);
            return 0;
        }
        if (layerCount <= 0) {
            log.info("Configuration " + config.getIdentifier() + " contained no layers.");
        }

        // Check whether there is any general service information
        if (this.serviceInformation == null) {
            log.debug("Reading service information.");
            this.serviceInformation = config.getServiceInformation();
        }
        return layerCount;
    }

    public ServiceInformation getServiceInformation() {
        return this.serviceInformation;
    }

    /**
     * @see org.springframework.beans.factory.DisposableBean#destroy()
     */
    public void destroy() throws Exception {
        //
    }

    /**
     * Finds out which {@link Configuration} contains the given layer,
     * {@link Configuration#removeLayer(String) removes} it, and returns the configuration, without
     * saving it.
     * <p>
     * The calling code is responsible from calling {@link Configuration#save()} on the returned
     * configuration object if the change is to be made persistent.
     * </p>
     * 
     * @param layerName
     *            the name of the layer to remove
     * @return the Configuration from which the layer has been removed, or {@code null} if no
     *         configuration contained such a layer
     */
    public synchronized Configuration removeLayer(final String layerName) throws IllegalArgumentException {
        for (Configuration config : configs) {
            if (config.removeLayer(layerName)) {
                return config;
            }
        }
        return null;
    }

    /**
     * Adds a layer and returns (but doesn't save) the {@link Configuration} to which the layer was
     * added.
     * 
     * @param tl
     *            the layer to add
     * @return the configuration to which the layer was added; calling code is in charge of decising
     *         whether to {@link Configuration#save() save} the configuration permanently or not.
     * @throws IllegalArgumentException
     *             if the given tile layer can't be added to any configuraion managed by this tile
     *             layer dispatcher.
     */
    public synchronized Configuration addLayer(final TileLayer tl) throws IllegalArgumentException {
        for (Configuration c : configs) {
            if (c.canSave(tl)) {
                c.addLayer(tl);
                return c;
            }
        }
        throw new IllegalArgumentException("No configuration found capable of saving " + tl);
    }

    /**
     * Replaces the given layer and returns the Layer's configuration, does not save the
     * configuration, the calling code shall do that if the change is to be made persistent.
     * 
     * @param tl
     * @throws IllegalArgumentException
     */
    public synchronized Configuration modify(final TileLayer tl) throws IllegalArgumentException {
        Configuration config = getConfiguration(tl);
        config.modifyLayer(tl);
        return config;
    }

    public Configuration getConfiguration(TileLayer tl) throws IllegalArgumentException {
        Assert.notNull(tl, "layer is null");
        return getConfiguration(tl.getId());
    }

    public Configuration getConfiguration(final String tileLayerId) throws IllegalArgumentException {
        Assert.notNull(tileLayerId, "tileLayerId is null");
        for (Configuration c : configs) {
            if (c.containsLayer(tileLayerId)) {
                return c;
            }
        }
        throw new IllegalArgumentException("No configuration found containing layer " + tileLayerId);
    }

    /**
     * Eliminates the gridset from the {@link GridSetBroker} and the {@link XMLConfiguration} only
     * if no layer references the given GridSet.
     * <p>
     * NOTE this method does not save the configuration, it's up to the calling code to do that in
     * order to make the change persistent.
     * </p>
     * 
     * @param gridSetName
     *            the gridset to remove.
     * @return the configuration modified after removing the gridset, or {@code null}
     * @throws IllegalStateException
     *             if there's any layer referencing the given GridSet
     * @throws IOException
     * @see {@link GridSetBroker#remove(String)}
     */
    public synchronized Configuration removeGridset(final String gridSetName)
            throws IllegalStateException, IOException {

        GridSet gridSet = gridSetBroker.get(gridSetName);
        if (gridSet == null) {
            return null;
        }
        List<String> refereningLayers = new ArrayList<String>();
        for (TileLayer layer : getLayerList()) {
            GridSubset gridSubset = layer.getGridSubset(gridSetName);
            if (gridSubset != null) {
                refereningLayers.add(layer.getName());
            }
        }
        if (refereningLayers.size() > 0) {
            throw new IllegalStateException("There are TileLayers referencing gridset '" + gridSetName + "': "
                    + refereningLayers.toString());
        }
        XMLConfiguration persistingConfig = getXmlConfiguration();
        GridSet removed = gridSetBroker.remove(gridSetName);
        Assert.notNull(removed != null);
        Assert.notNull(persistingConfig.removeGridset(gridSetName));

        return persistingConfig;
    }

    public synchronized void addGridSet(final GridSet gridSet) throws IllegalArgumentException, IOException {
        if (null != gridSetBroker.get(gridSet.getName())) {
            throw new IllegalArgumentException("GridSet " + gridSet.getName() + " already exists");
        }
        saveGridSet(gridSet);
    }

    private void saveGridSet(final GridSet gridSet) throws IOException {
        XMLConfiguration persistingConfig = getXmlConfiguration();
        persistingConfig.addOrReplaceGridSet(new XMLGridSet(gridSet));
        persistingConfig.save();
        gridSetBroker.put(gridSet);
    }

    private XMLConfiguration getXmlConfiguration() throws IllegalStateException {
        for (Configuration c : configs) {
            if (c instanceof XMLConfiguration) {
                return (XMLConfiguration) c;
            }
        }
        throw new IllegalStateException("Found no configuration of type " + XMLConfiguration.class.getName());
    }
}