Commit 364bc47c authored by Andrea Aime's avatar Andrea Aime
Browse files

Backporting the wps-download community module to 2.6.x

parent 6f7d0157
......@@ -52,6 +52,7 @@
<descriptor>release/ext-geofence.xml</descriptor>
<descriptor>release/ext-rest-ext.xml</descriptor>
<descriptor>release/ext-sldservice.xml</descriptor>
<descriptor>release/ext-wps-download.xml</descriptor>
</descriptors>
</configuration>
</plugin>
......@@ -166,6 +167,7 @@
<module>dyndimension</module>
<module>rest-ext</module>
<module>sldService</module>
<module>wps-download</module>
</modules>
</profile>
<profile>
......@@ -334,5 +336,11 @@
<module>geofence</module>
</modules>
</profile>
<profile>
<id>wps-download</id>
<modules>
<module>wps-download</module>
</modules>
</profile>
</profiles>
</project>
<assembly>
<id>wps-download-plugin</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>release/target/dependency</directory>
<outputDirectory></outputDirectory>
<includes>
<include>gs-wps-download*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>
......@@ -118,6 +118,11 @@
<artifactId>gs-sldservice</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.geoserver.community</groupId>
<artifactId>gs-wps-download</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
......
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd ">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.geoserver</groupId>
<artifactId>community</artifactId>
<version>2.6-SNAPSHOT</version>
</parent>
<groupId>org.geoserver.community</groupId>
<artifactId>gs-wps-download</artifactId>
<packaging>jar</packaging>
<version>2.6-SNAPSHOT</version>
<name>DownloadProcess</name>
<dependencies>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-process</artifactId>
<version>${gt.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-coverage</artifactId>
<version>${gt.version}</version>
</dependency>
<dependency>
<groupId>org.geoserver.extension</groupId>
<artifactId>gs-wps-core</artifactId>
<version>${project.version}</version>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.geoserver.extension</groupId>
<artifactId>gs-wps-core</artifactId>
<version>${gs.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.geoserver</groupId>
<artifactId>gs-main</artifactId>
<version>${gs.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.mockrunner</groupId>
<artifactId>mockrunner</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wps.gs.download;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.config.GeoServer;
import org.geotools.process.ProcessException;
import org.geotools.process.factory.DescribeParameter;
import org.geotools.process.factory.DescribeProcess;
import org.geotools.process.factory.DescribeResult;
import org.geotools.process.gs.GSProcess;
import org.geotools.util.logging.Logging;
import org.opengis.filter.Filter;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.util.ProgressListener;
import com.vividsolutions.jts.geom.Geometry;
/**
* The DownloadEstimatorProcess is used for checking if the download request does not exceeds the defined limits.
*
* @author "Alessio Fabiani - alessio.fabiani@geo-solutions.it"
*/
@DescribeProcess(title = "Estimator Process", description = "Checks if the input file does not exceed the limits")
public class DownloadEstimatorProcess implements GSProcess {
/** The Constant LOGGER. */
private static final Logger LOGGER = Logging.getLogger(DownloadEstimatorProcess.class);
private DownloadServiceConfigurationGenerator downloadServiceConfigurationGenerator;
/** The catalog. */
private final Catalog catalog;
/**
* @param readLimits
* @param writeLimits
* @param hardOutputLimit
* @param geoserver
*/
public DownloadEstimatorProcess(
DownloadServiceConfigurationGenerator downloadServiceConfigurationGenerator,
GeoServer geoserver) {
this.catalog = geoserver.getCatalog();
this.downloadServiceConfigurationGenerator = downloadServiceConfigurationGenerator;
}
/**
* This process returns a boolean value which indicates if the requested download does not exceed the imposed limits, if present
*
* @param layerName the layer name
* @param filter the filter
* @param email the email
* @param outputFormat the output format
* @param targetCRS the target crs
* @param roiCRS the roi crs
* @param roi the roi
* @param clip the crop to geometry
* @param progressListener the progress listener
* @return the boolean
* @throws Exception
*/
@DescribeResult(name = "result", description = "Download Limits are respected or not!")
public Boolean execute(
@DescribeParameter(name = "layerName", min = 1, description = "Original layer to download") String layerName,
@DescribeParameter(name = "filter", min = 0, description = "Optional Vectorial Filter") Filter filter,
@DescribeParameter(name = "targetCRS", min = 0, description = "Target CRS") CoordinateReferenceSystem targetCRS,
@DescribeParameter(name = "RoiCRS", min = 0, description = "Region Of Interest CRS") CoordinateReferenceSystem roiCRS,
@DescribeParameter(name = "ROI", min = 0, description = "Region Of Interest") Geometry roi,
@DescribeParameter(name = "cropToROI", min = 0, description = "Crop to ROI") Boolean clip,
ProgressListener progressListener) throws Exception {
//
// initial checks on mandatory params
//
// layer name
if (layerName == null || layerName.length() <= 0) {
throw new IllegalArgumentException("Empty or null layerName provided!");
}
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Estimator process called on resource: " + layerName);
}
if (clip == null) {
clip = false;
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Clipping disabled");
}
}
if (roi != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "ROI present");
}
DownloadUtilities.checkPolygonROI(roi);
if (roiCRS == null) {
throw new IllegalArgumentException("ROI without a CRS is not usable!");
}
roi.setUserData(roiCRS);
}
//
// Move on with the real code
//
// checking for the resources on the GeoServer catalog
LayerInfo layerInfo = catalog.getLayerByName(layerName);
if (layerInfo == null) {
// could not find any layer ... abruptly interrupt the process
throw new IllegalArgumentException("Unable to locate layer: " + layerName);
}
ResourceInfo resourceInfo = layerInfo.getResource();
if (resourceInfo == null) {
// could not find any data store associated to the specified layer ... abruptly interrupt the process
throw new IllegalArgumentException("Unable to locate ResourceInfo for layer:"
+ layerName);
}
//
// Get curent limits
//
DownloadServiceConfiguration limits = downloadServiceConfigurationGenerator
.getConfiguration();
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Getting configuration limits");
}
// ////
// 1. DataStore -> look for vectorial data download
// 2. CoverageStore -> look for raster data download
// ////
if (resourceInfo instanceof FeatureTypeInfo) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Working with Vectorial dataset");
}
final FeatureTypeInfo featureTypeInfo = (FeatureTypeInfo) resourceInfo;
return new VectorEstimator(limits).execute(featureTypeInfo, roi, clip, filter,
targetCRS, progressListener);
} else if (resourceInfo instanceof CoverageInfo) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Working with Raster dataset");
}
final CoverageInfo coverage = (CoverageInfo) resourceInfo;
return new RasterEstimator(limits).execute(progressListener, coverage, roi, targetCRS,
clip, filter);
}
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Working with a wrong Resource");
}
// the requeste layer is neither a featuretype nor a coverage --> error
final ProcessException ex = new ProcessException(
"Could not complete the Download Process: target resource is of Illegal type --> "
+ resourceInfo != null ? resourceInfo.getClass().getCanonicalName()
: "null");
// Notify the listener if present
if (progressListener != null) {
progressListener.exceptionOccurred(ex);
}
throw ex;
}
/**
* @return the {@link DownloadServiceConfiguration} containing the limits to check
*/
public DownloadServiceConfiguration getDownloadServiceConfiguration() {
return downloadServiceConfigurationGenerator.getConfiguration();
}
}
\ No newline at end of file
/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wps.gs.download;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.wps.ppio.ZipArchivePPIO;
import org.geoserver.wps.resource.WPSFileResource;
import org.geoserver.wps.resource.WPSResourceManager;
import org.geotools.process.ProcessException;
import org.geotools.process.factory.DescribeParameter;
import org.geotools.process.factory.DescribeProcess;
import org.geotools.process.factory.DescribeResult;
import org.geotools.process.gs.GSProcess;
import org.geotools.util.Utilities;
import org.geotools.util.logging.Logging;
import org.opengis.filter.Filter;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.util.ProgressListener;
import com.vividsolutions.jts.geom.Geometry;
/**
* The main DownloadProcess class.
*
* This class is simply responsible for deciding who is going to take care of the request and then for putting together the final result as a zip file
* adding the needed styles.
*
*
* @author "Alessio Fabiani - alessio.fabiani@geo-solutions.it"
* @author Simone Giannecchini, GeoSolutions SAS
*/
@SuppressWarnings("deprecation")
@DescribeProcess(title = "Enterprise Download Process", description = "Downloads Layer Stream and provides a ZIP.")
public class DownloadProcess implements GSProcess {
/** The LOGGER. */
private static final Logger LOGGER = Logging.getLogger(DownloadProcess.class);
/** The estimator. */
private final DownloadEstimatorProcess estimator;
/** The catalog. */
private final Catalog catalog;
private WPSResourceManager resourceManager;
/**
* Instantiates a new download process.
*
* @param geoServer the geo server
* @param sendMail the send mail
* @param estimator the estimator
* @param resourceManager the resourceManager to track resources to be cleaned up
*/
public DownloadProcess(GeoServer geoServer, DownloadEstimatorProcess estimator,
WPSResourceManager resourceManager) {
Utilities.ensureNonNull("geoServer", geoServer);
this.catalog = geoServer.getCatalog();
this.estimator = estimator;
this.resourceManager = resourceManager;
}
/**
* This process returns a zipped file containing the selected layer, cropped if needed.
*
* @param layerName the layer name
* @param filter the filter
* @param email the email
* @param mimeType the output format
* @param targetCRS the target crs
* @param roiCRS the roi crs
* @param roi the roi
* @param clip the crop to geometry
* @param progressListener the progress listener
* @return the file
* @throws ProcessException the process exception
*/
@DescribeResult(name = "result", description = "Zipped output files to download")
public File execute(
@DescribeParameter(name = "layerName", min = 1, description = "Original layer to download") String layerName,
@DescribeParameter(name = "filter", min = 0, description = "Optional Vector Filter") Filter filter,
@DescribeParameter(name = "outputFormat", min = 1, description = "Output Format Mime-Type") String mimeType,
@DescribeParameter(name = "targetCRS", min = 0, description = "Optional Target CRS") CoordinateReferenceSystem targetCRS,
@DescribeParameter(name = "RoiCRS", min = 0, description = "Optional Region Of Interest CRS") CoordinateReferenceSystem roiCRS,
@DescribeParameter(name = "ROI", min = 0, description = "Optional Region Of Interest (Polygon)") Geometry roi,
@DescribeParameter(name = "cropToROI", min = 0, description = "Crop to ROI") Boolean clip,
final ProgressListener progressListener) throws ProcessException {
try {
//
// initial checks on mandatory params
//
// layer name
if (layerName == null || layerName.length() <= 0) {
throw new IllegalArgumentException("Empty or null layerName provided!");
}
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Download process called on resource: " + layerName);
}
// Default behavior is intersection
if (clip == null) {
clip = false;
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Clipping disabled");
}
}
// Checking the validity of the input ROI
if (roi != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "ROI check");
}
DownloadUtilities.checkPolygonROI(roi);
if (roiCRS == null) {
throw new IllegalArgumentException("ROI without a CRS is not usable!");
}
roi.setUserData(roiCRS);
}
//
// do we respect limits?
//
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Running the estimator");
}
if (!estimator.execute(layerName, filter, targetCRS, roiCRS, roi, clip,
progressListener)) {
throw new IllegalArgumentException("Download Limits Exceeded. Unable to proceed!");
}
//
// Move on with the real code
//
// checking for the resources on the GeoServer catalog
LayerInfo layerInfo = catalog.getLayerByName(layerName);
if (layerInfo == null) {
// could not find any layer ... abruptly interrupt the process
throw new IllegalArgumentException("Unable to locate layer: " + layerName);
}
ResourceInfo resourceInfo = layerInfo.getResource();
if (resourceInfo == null) {
// could not find any data store associated to the specified layer ... abruptly interrupt the process
throw new IllegalArgumentException("Unable to locate ResourceInfo for layer:"
+ layerName);
}
//
// Limits
//
DownloadServiceConfiguration limits = estimator.getDownloadServiceConfiguration();
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Employing limits " + limits);
}
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "The resource to work on is " + resourceInfo.getName());
}
// CORE CODE
File internalOutput = null;
if (resourceInfo instanceof FeatureTypeInfo) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "The resource to work on is a vector layer");
}
//
// VECTOR
//
// perform the actual download of vectorial data accordingly to the request inputs
internalOutput = new VectorDownload(limits, resourceManager).execute(
(FeatureTypeInfo) resourceInfo, mimeType, roi, clip, filter, targetCRS,
progressListener);
} else if (resourceInfo instanceof CoverageInfo) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "The resource to work on is a raster layer");
}
//
// RASTER
//
CoverageInfo cInfo = (CoverageInfo) resourceInfo;
// convert/reproject/crop if needed the coverage
internalOutput = new RasterDownload(limits, resourceManager).execute(mimeType,
progressListener, cInfo, roi, targetCRS, clip, filter);
} else {
// wrong type
throw new IllegalArgumentException(
"Could not complete the Download Process, requested layer was of wrong type-->"
+ resourceInfo.getClass());
}
//
// Work on result
//
// checks
if (internalOutput == null) {
// wrong type
throw new IllegalStateException(
"Could not complete the Download Process, output file is null");
}
if (!internalOutput.exists() || !internalOutput.canRead()) {
// wrong type
throw new IllegalStateException(
"Could not complete the Download Process, output file invalid! --> "
+ internalOutput.getAbsolutePath());
}
// adding the style and zipping
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Preparing the result");
}
// build output zip
final File result = resourceManager.getOutputFile(
resourceManager.getExecutionId(true), resourceInfo.getName() + ".zip");
FileOutputStream os1 = null;
try {
os1 = new FileOutputStream(result);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Listing files");
}
// output
List<File> filesToDownload = new ArrayList<File>();
filesToDownload.add(internalOutput);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Collecting styles");
}
// add all SLD to zip
List<File> styles = DownloadUtilities.collectStyles(layerInfo);
filesToDownload.addAll(styles);