org.geoserver.wfs.xslt.config.TransformRepository.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.wfs.xslt.config.TransformRepository.java

Source

/* Copyright (c) 2001 - 2013 OpenPlans - www.openplans.org. All rights reserved.
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.wfs.xslt.config;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.transform.ErrorListener;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.impl.FeatureTypeInfoImpl;
import org.geoserver.config.GeoServerDataDirectory;
import org.geoserver.wfs.WFSException;
import org.geotools.util.logging.Logging;

import com.thoughtworks.xstream.XStream;

/**
 * Manages access to the XLST transformation definitions
 * 
 * @author Andrea Aime - GeoSolutions
 */
public class TransformRepository {

    static final Logger LOGGER = Logging.getLogger(TransformRepository.class);

    static final FilenameFilter CONFIG_NAME_FILTER = new SuffixFileFilter(".xml");

    XStream xs;

    GeoServerDataDirectory dataDir;

    /**
     * Caches the {@link TransformInfo} objects so that we don't have to load them from disk all the time
     */
    FileItemCache<TransformInfo> infoCache = new FileItemCache<TransformInfo>(100) {

        @Override
        protected TransformInfo loadItem(File file) throws IOException {
            if (!file.exists()) {
                return null;
            }

            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                TransformInfo info = (TransformInfo) xs.fromXML(fis);
                info.setName(getTransformName(file));

                return info;
            } finally {
                IOUtils.closeQuietly(fis);
            }
        }
    };

    /**
     * Caches the XSLT Templates to avoid parsing the XSLT over and over (Templates is thread safe,
     * {@link Transformer} is not.
     */
    FileItemCache<Templates> transformCache = new FileItemCache<Templates>(100) {

        @Override
        protected Templates loadItem(File file) throws IOException {
            try {
                Source xslSource = new StreamSource(file);

                TransformerFactory tf = TransformerFactory.newInstance();
                final List<TransformerException> errors = new ArrayList<TransformerException>();
                tf.setErrorListener(new ErrorListener() {

                    @Override
                    public void warning(TransformerException e) throws TransformerException {
                        LOGGER.log(Level.WARNING, "Found warning while loading XSLT template", e);

                    }

                    @Override
                    public void fatalError(TransformerException e) throws TransformerException {
                        errors.add(e);
                    }

                    @Override
                    public void error(TransformerException e) throws TransformerException {
                        errors.add(e);

                    }
                });
                Templates template = tf.newTemplates(xslSource);

                if (errors.size() > 0) {
                    StringBuilder sb = new StringBuilder("Errors found in the template");
                    for (TransformerException e : errors) {
                        sb.append("\n").append(e.getMessageAndLocation());
                    }

                    throw new IOException(sb.toString());
                }

                return template;
            } catch (TransformerException e) {
                throw new IOException("Error found in the template: " + e.getMessageAndLocation());
            }
        }
    };

    public TransformRepository(GeoServerDataDirectory dataDir, Catalog catalog) {
        this.dataDir = dataDir;
        initXStream(catalog);
    }

    /**
     * Sets up xstream to get nice xml output
     * 
     * @param catalog
     */
    private void initXStream(Catalog catalog) {
        xs = new XStream();
        xs.omitField(TransformInfo.class, "name");
        xs.alias("transform", TransformInfo.class);
        xs.registerLocalConverter(TransformInfo.class, "featureType",
                new ReferenceConverter(FeatureTypeInfo.class, catalog));
        xs.addDefaultImplementation(FeatureTypeInfoImpl.class, FeatureTypeInfo.class);
    }

    /**
     * The transform name is the same as the config file, minus the extension
     * 
     * @param file
     * @return
     */
    protected String getTransformName(File file) {
        String name = file.getName();
        int idx = name.indexOf(".");
        if (idx > 0) {
            return name.substring(0, idx);
        } else {
            return name;
        }
    }

    /**
     * Returns all the transform (either global or feature type specific)
     * 
     * @return
     */
    public List<TransformInfo> getAllTransforms() throws IOException {
        File root = dataDir.findOrCreateDir("wfs", "transform");
        List<TransformInfo> result = new ArrayList<TransformInfo>();
        for (File f : root.listFiles(CONFIG_NAME_FILTER)) {
            try {
                TransformInfo tx = infoCache.getItem(f);
                result.add(tx);
            } catch (Exception e) {
                LOGGER.log(Level.FINE, "Failed to load configuration from file " + f.getAbsolutePath(), e);
            }
        }

        return result;
    }

    /**
     * Returns all the global transformations (not attached to a particular layer)
     * 
     * @return
     */
    public List<TransformInfo> getGlobalTransforms() throws IOException {
        List<TransformInfo> allTransformations = getAllTransforms();
        List<TransformInfo> result = new ArrayList<TransformInfo>();
        for (TransformInfo ti : allTransformations) {
            if (ti.getFeatureType() == null) {
                result.add(ti);
            }
        }

        return result;
    }

    /**
     * Returns transformations associated to a specific feature type
     * 
     * @param featureType
     * @return
     */
    public List<TransformInfo> getTypeTransforms(FeatureTypeInfo featureType) throws IOException {
        List<TransformInfo> allTransformations = getAllTransforms();
        List<TransformInfo> result = new ArrayList<TransformInfo>();
        for (TransformInfo ti : allTransformations) {
            if (ti.getFeatureType() != null && ti.getFeatureType().getId().equals(featureType.getId())) {
                result.add(ti);
            }
        }

        return result;
    }

    /**
     * Returns a specific transformation by hand
     * 
     * @param name
     * @return
     */
    public TransformInfo getTransformInfo(String name) throws IOException {
        File infoFile = getTransformInfoFile(name);
        return infoCache.getItem(infoFile);
    }

    /**
     * Deletes a transformation definition and its associated XSLT file (assuming the
     * latter is not shared with other transformations)
     * 
     * @param info
     * @return
     * @throws IOException
     */
    public boolean removeTransformInfo(TransformInfo info) throws IOException {
        File infoFile = getTransformInfoFile(info.getName());
        boolean result = infoFile.delete();

        File xsltFile = getTransformFile(info);
        infoCache.removeItem(infoFile);

        boolean shared = false;
        if (xsltFile.exists()) {
            for (TransformInfo ti : getAllTransforms()) {
                File curr = getTransformFile(ti);
                if (curr.equals(xsltFile)) {
                    shared = true;
                    break;
                }
            }
        }
        if (!shared) {
            result = result && xsltFile.delete();
            transformCache.removeItem(xsltFile);
        }

        return result;
    }

    /**
     * Returns the XSLT transformer for a specific {@link TransformInfo}
     * 
     * @param name
     * @return
     */
    public Transformer getTransformer(TransformInfo info) throws IOException {
        File txFile = getTransformFile(info);

        Templates templates = transformCache.getItem(txFile);
        if (templates != null) {
            try {
                return templates.newTransformer();
            } catch (TransformerConfigurationException e) {
                throw new WFSException("Failed to load XSLT transformation " + info.getXslt(), e);
            }
        } else {
            throw new IOException("No XLST found at " + txFile.getAbsolutePath());
        }
    }

    /**
     * Returns the stylesheet of a transformation. It is the duty of the caller to close the input stream after reading it.
     * @return
     * @throws IOException
     */
    public InputStream getTransformSheet(TransformInfo info) throws IOException {
        File txFile = getTransformFile(info);

        return new FileInputStream(txFile);
    }

    /**
     * Writes the stylesheet of a transformation. This method will close the provided input stream.
     * 
     * @param info
     * @param sheet
     * @throws IOException
     */
    public void putTransformSheet(TransformInfo info, InputStream sheet) throws IOException {
        File txFile = getTransformFile(info);
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(txFile);
            IOUtils.copy(sheet, fos);
        } finally {
            IOUtils.closeQuietly(sheet);
            IOUtils.closeQuietly(fos);
        }

    }

    /**
     * Saves/updates the specified transformation
     * 
     * @param transform
     * @throws IOException
     */
    public void putTransformInfo(TransformInfo transform) throws IOException {
        if (transform.getName() == null) {
            throw new IllegalArgumentException("Transformation does not have a name set");
        }
        File file = getTransformInfoFile(transform.getName());

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            xs.toXML(transform, fos);
        } finally {
            IOUtils.closeQuietly(fos);
        }

        infoCache.put(transform, file);
    }

    File getTransformInfoFile(String name) throws IOException {
        File root = dataDir.findOrCreateDir("wfs", "transform");
        File infoFile = new File(root, name + ".xml");
        return infoFile;
    }

    private File getTransformFile(TransformInfo info) throws IOException {
        String txName = info.getXslt();
        File root = dataDir.findOrCreateDir("wfs", "transform");
        File txFile = new File(root, txName);
        return txFile;
    }

}