From 86a3de1e2d4a971d028211816a67730c20671fc9 Mon Sep 17 00:00:00 2001 From: andypower Date: Fri, 20 Mar 2015 18:05:00 +0100 Subject: [PATCH] Adding the publishStyle(..., boolean raw) method to the GeoServerRESTPublishe class for compatibility reasons --- .../rest/GeoServerRESTPublisher.java | 5709 +++++++++-------- .../manager/GeoServerRESTStyleManager.java | 9 + 2 files changed, 2870 insertions(+), 2848 deletions(-) diff --git a/src/main/java/it/geosolutions/geoserver/rest/GeoServerRESTPublisher.java b/src/main/java/it/geosolutions/geoserver/rest/GeoServerRESTPublisher.java index 374bc8b..adbeedd 100644 --- a/src/main/java/it/geosolutions/geoserver/rest/GeoServerRESTPublisher.java +++ b/src/main/java/it/geosolutions/geoserver/rest/GeoServerRESTPublisher.java @@ -1,2848 +1,2861 @@ -/* - * GeoServer-Manager - Simple Manager Library for GeoServer - * - * Copyright (C) 2007,2013 GeoSolutions S.A.S. - * http://www.geo-solutions.it - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package it.geosolutions.geoserver.rest; - -import it.geosolutions.geoserver.rest.decoder.RESTCoverage; -import it.geosolutions.geoserver.rest.decoder.RESTCoverageStore; -import it.geosolutions.geoserver.rest.decoder.RESTStructuredCoverageGranulesList; -import it.geosolutions.geoserver.rest.decoder.RESTStyleList; -import it.geosolutions.geoserver.rest.decoder.utils.NameLinkElem; -import it.geosolutions.geoserver.rest.encoder.GSBackupEncoder; -import it.geosolutions.geoserver.rest.encoder.GSLayerEncoder; -import it.geosolutions.geoserver.rest.encoder.GSLayerGroupEncoder; -import it.geosolutions.geoserver.rest.encoder.GSNamespaceEncoder; -import it.geosolutions.geoserver.rest.encoder.GSPostGISDatastoreEncoder; -import it.geosolutions.geoserver.rest.encoder.GSResourceEncoder; -import it.geosolutions.geoserver.rest.encoder.GSResourceEncoder.ProjectionPolicy; -import it.geosolutions.geoserver.rest.encoder.GSWorkspaceEncoder; -import it.geosolutions.geoserver.rest.encoder.coverage.GSCoverageEncoder; -import it.geosolutions.geoserver.rest.encoder.feature.GSFeatureTypeEncoder; -import it.geosolutions.geoserver.rest.manager.GeoServerRESTStructuredGridCoverageReaderManager; -import it.geosolutions.geoserver.rest.manager.GeoServerRESTStructuredGridCoverageReaderManager.ConfigureCoveragesOption; -import it.geosolutions.geoserver.rest.manager.GeoServerRESTStyleManager; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.net.URLEncoder; -import java.util.zip.ZipFile; - -import org.apache.commons.httpclient.NameValuePair; -import org.apache.commons.io.FilenameUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Connect to a GeoServer instance to publish or modify its contents via REST API. - *

- * There are no modifiable instance fields, so all the calls are thread-safe. - * - * @author ETj (etj at geo-solutions.it) - * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it - */ -public class GeoServerRESTPublisher { - - public static final String DEFAULT_CRS = "EPSG:4326"; - - /** The logger for this class */ - private static final Logger LOGGER = LoggerFactory.getLogger(GeoServerRESTPublisher.class); - - /** - * GeoServer instance base URL. E.g.: http://localhost:8080/geoserver. - */ - private final String restURL; - - /** - * GeoServer instance privileged username, with read & write permission on REST API - */ - private final String gsuser; - - /** - * GeoServer instance password for privileged username with r&w permission on REST API - */ - private final String gspass; - - - private final GeoServerRESTStyleManager styleManager; - /** - * Creates a GeoServerRESTPublisher to connect against a GeoServer instance with the given URL and user credentials. - * - * @param restURL the base GeoServer URL (e.g.: http://localhost:8080/geoserver) - * @param username auth credential - * @param password auth credential - */ - public GeoServerRESTPublisher(String restURL, String username, String password) { - this.restURL = HTTPUtils.decurtSlash(restURL); - this.gsuser = username; - this.gspass = password; - - URL url = null; - try { - url = new URL(restURL); - } catch (MalformedURLException ex) { - LOGGER.error("Bad URL: Calls to GeoServer are going to fail" , ex); - } - styleManager = new GeoServerRESTStyleManager(url, username, password); - } - - // ========================================================================== - // === BACKUP and RESTORE - // ========================================================================== - - /** - * Issues a GeoServer BACKUP. - *

- * Won't include data, cached tiles, or logs. Use {@link #backup(String, boolean, boolean, boolean)} to control these parameters. - * - * @param backupDir the target Backup Dir String. - * - * @return id of the backup. - * @throws IllegalArgumentException if the backupDir is null or empty - */ - public String backup(final String backupDir) throws IllegalArgumentException { - /* - * This is the equivalent call with cUrl: - * - * {@code curl -u admin:geoserver -XPOST \ -H 'Content-type: text/xml' \ --data - * "<task><path>${BACKUP_DATADIR}</path></task>" \ ${restURL}/rest/bkprst/backup} - */ - return backup(backupDir, false, false, false); - } - - /** - * Issues a GeoServer BACKUP. - * - * @param backupDir the target Backup Dir String. - * @param includedata whether or not include the data dir Boolean. - * @param includegwc whether or not include the geowebcache dir Boolean. - * @param includelog whether or not include the log dir Boolean. - * - * @return id of the backup. - * @throws IllegalArgumentException if the backupDir is null or empty. - */ - public String backup(final String backupDir, final boolean includedata, - final boolean includegwc, final boolean includelog) throws IllegalArgumentException { - /* - * This is the equivalent call with cUrl: - * - * {@code curl -u admin:geoserver -XPOST \ -H 'Content-type: text/xml' \ --data - * "<task><path>${BACKUP_DATADIR}</path><includedata>${includedata}</includedata><includegwc>${includegwc}</includegwc><includelog>${includelog}</includelog></task>" - * \ ${restURL}/rest/bkprst/backup} - */ - if ((backupDir == null) || backupDir.isEmpty()) { - throw new IllegalArgumentException("The backup_dir must not be null or empty"); - } - - StringBuilder bkpUrl = new StringBuilder(restURL); - bkpUrl.append("/rest/bkprst/backup"); - - final GSBackupEncoder bkpenc = new GSBackupEncoder(backupDir); - bkpenc.setIncludeData(includedata); - bkpenc.setIncludeGwc(includegwc); - bkpenc.setIncludeLog(includelog); - final String result = HTTPUtils.post(bkpUrl.toString(), bkpenc.toString(), "text/xml", - gsuser, gspass); - - return result; - } - - /** - * Issues a GeoServer RESTORE. - * - * @param backupDir the source backup dir. - * - * @return id of the backup. - * @throws IllegalArgumentException if the backupDir is null or empty - */ - public String restore(final String backupDir) throws IllegalArgumentException { - /* - * This is the equivalent call with cUrl: - * - * {@code curl -u admin:geoserver -XPOST \ -H 'Content-type: text/xml' \ --data - * "<task><path>${BACKUP_DATADIR}</path></task>" \ ${restURL}/rest/bkprst/restore} - */ - if ((backupDir == null) || backupDir.isEmpty()) { - throw new IllegalArgumentException("The backup_dir must not be null or empty"); - } - - StringBuilder bkpUrl = new StringBuilder(restURL); - bkpUrl.append("/rest/bkprst/restore"); - - final GSBackupEncoder bkpenc = new GSBackupEncoder(backupDir); - - final String result = HTTPUtils.post(bkpUrl.toString(), bkpenc.toString(), "text/xml", - gsuser, gspass); - - return result; - } - - // ========================================================================== - // === WORKSPACES - // ========================================================================== - - /** - * Create a new Workspace. - *

- * GeoServer will automatically create an associated Namespace with the URI being "http://{workspaceName}". To specify a custom Namespace URI, use - * {@link #createWorkspace(String, URI)}. - * - * @param workspace The name of the new workspace. - * - * @return true if the workspace was created. - */ - public boolean createWorkspace(final String workspace) { - /* - * This is the equivalent call with cUrl: - * - * {@code curl -u admin:geoserver -XPOST \ -H 'Content-type: text/xml' \ -d "$WORKSPACE" \ - * http://$GSIP:$GSPORT/$SERVLET/rest/workspaces } - */ - final String sUrl = restURL + "/rest/workspaces"; - final GSWorkspaceEncoder wsenc = new GSWorkspaceEncoder(workspace); - final String wsxml = wsenc.toString(); - final String result = HTTPUtils.postXml(sUrl, wsxml, gsuser, gspass); - return result != null; - } - - /** - * Create both a workspace and its associated namespace. - *

- * Note that this method is equivalent to {@link #createNamespace}. - * - * @param name Name for the new workspace, which will be also its associated namespace prefix. - * @param uri Namespace URI. Cannot be empty. - * @return true if the Workspace and its associated namespace were successfully created. - */ - public boolean createWorkspace(final String name, final URI uri) { - // This is really an alias to createNamespace, as GeoServer - // will automatically create the associated workspace as well. - return createNamespace(name, uri); - } - - // ========================================================================== - // === NAMESPACES - // ========================================================================== - - /** - * Create a new Namespace. GeoServer will automatically create the corresponding Workspace. - * - * Prefix and URI are mandatory and cannot be empty. If a Namespace with the given prefix already exists, it won't be created. - * - * @param prefix The name of the new Namespace. - * @param uri The URI of the new Namespace. - * - * @return true if the Namespace was successfully created. - * @see GeoServer Documentation - */ - public boolean createNamespace(final String prefix, final URI uri) { - final String sUrl = restURL + "/rest/namespaces"; - final GSNamespaceEncoder nsenc = new GSNamespaceEncoder(prefix, uri); - final String nsxml = nsenc.toString(); - final String result = HTTPUtils.postXml(sUrl, nsxml, gsuser, gspass); - return result != null; - } - - /** - * Update a Namespace URI. - * - * Prefix and URI are mandatory and cannot be empty. A Namespace with the given prefix should exist. - * - * @param prefix The prefix of an existing Namespace. - * @param uri The new URI. - * - * @return true if the Namespace was successfully updated. - */ - public boolean updateNamespace(final String prefix, final URI uri) { - final String sUrl = restURL + "/rest/namespaces/" + encode(prefix); - final GSNamespaceEncoder nsenc = new GSNamespaceEncoder(prefix, uri); - final String nsxml = nsenc.toString(); - final String result = HTTPUtils.put(sUrl, nsxml, "application/xml", gsuser, gspass); - return result != null; - } - - /** - * Remove a given Namespace. It will remove the associated Workspace as well. - * - * @param prefix The Namespace prefix - * @param recurse The recurse parameter is used to recursively delete all resources contained in the workspace associated with this Namespace. - * This includes data stores, coverage stores, feature types, etc... Allowable values for this parameter are true or false. - * The default (safer) value is false. - * - * @return true if the Namespace was successfully removed. - */ - public boolean removeNamespace(final String prefix, boolean recurse) { - // Hack: We are instead calling removeWorkspace, as DELETE on - // a namespace will leave associated workspace in an inconsistent - // state. See https://jira.codehaus.org/browse/GEOS-5075 - // TODO switch to namespace when GEOS-5075 is solved - return removeWorkspace(prefix, recurse); - } - - // ========================================================================== - // === STYLES - // ========================================================================== - - /** - * Store and publish a Style. - * - * @param sldBody the full SLD document as a String. - * - * @return true if the operation completed successfully. - */ - public boolean publishStyle(String sldBody) { - return styleManager.publishStyle(sldBody); - } - - /** - * Store and publish a Style, assigning it a name. - * - * @param sldBody the full SLD document as a String. - * @param name the Style name. - * - * @return true if the operation completed successfully. - * @throws IllegalArgumentException if the style body is null or empty. - */ - public boolean publishStyle(final String sldBody, final String name) - throws IllegalArgumentException { - return styleManager.publishStyle(sldBody, name); - } - - /** - * Store and publish a Style. - * - * @param sldFile the File containing the SLD document. - * - * @return true if the operation completed successfully. - */ - public boolean publishStyle(File sldFile) { - return styleManager.publishStyle(sldFile); - } - - /** - * Store and publish a Style, assigning it a name. - * - * @param sldFile the File containing the SLD document. - * @param name the Style name. - * - * @return true if the operation completed successfully. - */ - public boolean publishStyle(File sldFile, String name) { - return styleManager.publishStyle(sldFile, name); - } - - /** - * Update a Style. - * - * @param sldBody the new SLD document as a String. - * @param name the Style name to update. - * - * @return true if the operation completed successfully. - * @throws IllegalArgumentException if the style body or name are null or empty. - */ - public boolean updateStyle(final String sldBody, final String name) - throws IllegalArgumentException { - return styleManager.updateStyle(sldBody, name); - } - - /** - * Update a Style. - * - * @param sldFile the File containing the SLD document. - * @param name the Style name. - * - * @return true if the operation completed successfully. - * @throws IllegalArgumentException if the sldFile file or name are null or name is empty. - */ - public boolean updateStyle(final File sldFile, final String name) - throws IllegalArgumentException { - - return styleManager.updateStyle(sldFile, name); - } - - /** - * Remove a Style. - *

- * The Style will be unpublished, and (optionally) the SLD file will be removed. - * - * @param styleName the name of the Style to remove. - * @param purge remove the related SLD file from disk. - * - * @return true if the operation completed successfully. - * @throws IllegalArgumentException if styleName is null or empty. - */ - public boolean removeStyle(String styleName, final boolean purge) - throws IllegalArgumentException { - - return styleManager.removeStyle(styleName, purge); - } - - /** - * Remove a Style. - *

- * The Style will be unpublished and the related SLD file will be removed. - * - * @param styleName the name of the Style to remove. - * - * @return true if the operation completed successfully. - */ - public boolean removeStyle(String styleName) { - return styleManager.removeStyle(styleName); - } - - /** - * @since GeoServer 2.2 - * @see GeoServerRESTStyleManager# - */ - public boolean publishStyleInWorkspace(String workspace, String sldBody) { - return styleManager.publishStyleInWorkspace(workspace, sldBody); - } - - /** - * @since GeoServer 2.2 - * @see GeoServerRESTStyleManager# - */ - public boolean publishStyleInWorkspace(String workspace, String sldBody, String name) throws IllegalArgumentException { - return styleManager.publishStyleInWorkspace(workspace, sldBody, name); - } - - /** - * @since GeoServer 2.2 - * @see GeoServerRESTStyleManager#publishStyleInWorkspace(java.lang.String, java.io.File) - */ - public boolean publishStyleInWorkspace(String workspace, File sldFile) { - return styleManager.publishStyleInWorkspace(workspace, sldFile); - } - - /** - * @since GeoServer 2.2 - * @see GeoServerRESTStyleManager#publishStyleInWorkspace(java.lang.String, java.io.File, java.lang.String) - */ - public boolean publishStyleInWorkspace(String workspace, File sldFile, String name) { - return styleManager.publishStyleInWorkspace(workspace, sldFile, name); - } - - /** - * @since GeoServer 2.2 - * @see GeoServerRESTStyleManager#updateStyleInWorkspace(java.lang.String, java.lang.String, java.lang.String) - */ - public boolean updateStyleInWorkspace(String workspace, String sldBody, String name) throws IllegalArgumentException { - return styleManager.updateStyleInWorkspace(workspace, sldBody, name); - } - - /** - * @since GeoServer 2.2 - * @see GeoServerRESTStyleManager#updateStyleInWorkspace(java.lang.String, java.io.File, java.lang.String) - */ - public boolean updateStyleInWorkspace(String workspace, File sldFile, String name) throws IllegalArgumentException { - return styleManager.updateStyleInWorkspace(workspace, sldFile, name); - } - - /** - * @since GeoServer 2.2 - * @see GeoServerRESTStyleManager#removeStyleInWorkspace(java.lang.String, java.lang.String, boolean) - */ - public boolean removeStyleInWorkspace(String workspace, String styleName, boolean purge) throws IllegalArgumentException { - return styleManager.removeStyleInWorkspace(workspace, styleName, purge); - } - - /** - * @since GeoServer 2.2 - * @see GeoServerRESTStyleManager#removeStyleInWorkspace(java.lang.String, java.lang.String) - */ - public boolean removeStyleInWorkspace(String workspace, String styleName) { - return styleManager.removeStyleInWorkspace(workspace, styleName); - } - - - // ========================================================================== - // === DATASTORE PUBLISHING - // ========================================================================== - - /** - * - * @author cancellieri - * @deprecated use {@link StoreType} - */ - public enum DataStoreType { - /** - * Raster based data sources. - */ - COVERAGESTORES, - /** - * Vector based data sources. Can be a file in the case of a Shapefile, a database connection in the case of PostGIS, or a server in the case - * of a remote Web Feature Service. - */ - DATASTORES; - - /** - * @deprecated use {@link StoreType#getTypeNameWithFormat(StoreType, Format)} - * @param type - * @return - */ - public static String getTypeName(StoreType type) { - return StoreType.getTypeNameWithFormat(type, Format.XML); - } - - /** - * @deprecated use {@link StoreType#toString()} - */ - @Override - public String toString() { - return this.name().toLowerCase(); - } - } - - /** - * DataStoreType definitions. - *

- * - * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it - */ - public enum StoreType { - /** - * Raster based data sources. - */ - COVERAGESTORES, - /** - * Vector based data sources. Can be a file in the case of a Shapefile, a database connection in the case of PostGIS, or a server in the case - * of a remote Web Feature Service. - */ - DATASTORES; - - /** - * Get the type name of a StoreType with the specified format. - * - * @param type the StoreType. - * @param format see {@link Format} - * @return "featureTypes.{xml|html|...}" for DATASTORES, "coverages.{xml|html|...}" otherwise. - */ - public static String getTypeNameWithFormat(StoreType type, Format format) { - return getTypeName(type) + "." + format; - } - - /** - * Get the type name of a StoreType with the specified format. - * - * @param type the StoreType. - * @param format see {@link Format} - * @return "featuretypes.{xml|html|...}" for DATASTORES, "coverages.{xml|html|...}" otherwise. - */ - public String getTypeNameWithFormat(Format format) { - return getTypeName(this).toLowerCase() + "." + format; - } - - /** - * Get the type name of a StoreType. - * - * @param type the StoreType. - * @return "featureTypes" for DATASTORES, "coverages" otherwise. - */ - public static String getTypeName(StoreType type) { - switch (type) { - case COVERAGESTORES: - return "coverages"; // Format - case DATASTORES: - return "featureTypes"; - default: - return "coverages"; - } - } - - /** - * Get the type name of a StoreType. - * - * @param type the StoreType. - * @return "dataStore" for DATASTORES, "coverageStore" otherwise. - */ - public static String getType(StoreType type) { - switch (type) { - case COVERAGESTORES: - return "coverageStore"; // Format - case DATASTORES: - return "dataStore"; - default: - return "coverageStore"; - } - } - - /** - * Get the type name of a StoreType. - * - * @return "featuretypes" for DATASTORES, "coverages" otherwise. - */ - public String getTypeName() { - return getTypeName(this); - } - - /** - * Get the type of a StoreType. - * - * @param type the StoreType. - * @return "dataStore" for DATASTORES, "coverageStore" otherwise. - */ - public String getType() { - return getType(this); - } - - /** - * Returns a lowercase representation of the parameter value, suitable to construct the rest call. - */ - @Override - public String toString() { - return this.name().toLowerCase(); - } - } - - /** - * Specifies the method used to publish a resource. - * - * - * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it - */ - public enum UploadMethod { - /** - * directly upload a file from a local source. The body of the request is the file itself. - */ - FILE, @Deprecated - file, - /** - * indirectly upload a file from a remote source. The body of the request is the URL where the data is published. This url must be visible - * from the server. - */ - URL, @Deprecated - url, - /** - * forgo upload, and use an existing file on the server. The body of the request is the absolute local path to the existing file. - */ - EXTERNAL, @Deprecated - external; - - /** - * Returns a lowercase representation of the parameter value, suitable to construct the rest call. - */ - @Override - public String toString() { - return this.name().toLowerCase(); - } - } - - /** - * Vector data format being uploaded. Following extensions are supported: - * - * - * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it - */ - public enum DataStoreExtension { - /** Shapefile */ - SHP, - /** Properties file */ - PROPERTIES, - /** H2 Database */ - H2, - /** SpatiaLite Database */ - SPATIALITE; - - /** - * Returns a lowercase representation of the parameter value, suitable to construct the rest call. - */ - @Override - public String toString() { - return this.name().toLowerCase(); - } - } - - /** - * Raster data format being uploaded. Following extensions are supported: - * - * - * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it - */ - public enum CoverageStoreExtension { - /** GeoTiff coverage */ - GEOTIFF, - /** ImageMosaic */ - IMAGEMOSAIC, - /** Geo referenced image (JPEG,PNG,TIF) */ - WORLDIMAGE; - - /** - * Returns a lowercase representation of the parameter value, suitable to construct the rest call. - */ - @Override - public String toString() { - return this.name().toLowerCase(); - } - } - - /** - * Control if feature types are to be automatically configured upon file upload. It can take one of the three values: - * - */ - public static enum ParameterConfigure { - /** Configure first feature type only (default). */ - FIRST, - /** Don't configure any feature types. */ - NONE, - /** Configure all feature types. */ - ALL; - - /** - * Returns a lowercase representation of the parameter value, suitable to construct the rest call. - */ - @Override - public String toString() { - return this.name().toLowerCase(); - } - } - - /** - * Upload and publish data (vector or raster), and automatically create the associated Store if needed. - * - * This is a generic method; use {@link #createDataStore} and {@link #createCoverageStore} for vector and raster publishing respectively. - * - * @param workspace workspace where data will be published. - * @param dsType Store type. See {@link DataStoreType}. - * @param storeName Store name. Will be created if doesn't exist. - * @param method Publication method. See {@link UploadMethod}. - * @param extension Dataset format. One of {@link DataStoreExtension} for vector data, or one of {@link CoverageStoreExtension} for raster data. - * @param mimeType Dataset MIME type. - * @param uri The data location. A local path for {@link UploadMethod#FILE}, a server path for {@link UploadMethod#EXTERNAL}, or a URL for - * {@link UploadMethod#URL}. - * @param configure Indicate if associated feature types will be automatically configured. See {@link ParameterConfigure}. - * @param params Other parameters to be included in request, such as "update", "charset" or "coverageName". See GeoServer REST documentation for - * details: dataStore and coverageStore operations. - * @return {@code true} if the store was successfully created. - * @throws FileNotFoundException if local file does not exist when using the {@link UploadMethod#FILE} method. - * @throws IllegalArgumentException if any of the mandatory {@code workspace}, {@code dsType}, {@code storeName}, {@code method}, - * {@code extension}, {@code mimeType} or {@code uri} parameters are {@code null}. - */ - private boolean createStore(String workspace, StoreType dsType, String storeName, - UploadMethod method, Enum extension, String mimeType, URI uri, - ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException, - IllegalArgumentException { - if (workspace == null || dsType == null || storeName == null || method == null - || extension == null || mimeType == null || uri == null) { - throw new IllegalArgumentException("Null argument"); - } - StringBuilder sbUrl = new StringBuilder(restURL).append("/rest/workspaces/") - .append(workspace).append("/").append(dsType).append("/").append(storeName) - .append("/").append(method).append(".").append(extension); - - if (configure != null) { - sbUrl.append("?configure=").append(configure); - if (params != (NameValuePair[]) null) { - final String paramString = appendParameters(params); - if (!paramString.isEmpty()) { - sbUrl.append("&").append(paramString); - } - } - } - - String sentResult = null; - - if (method.equals(UploadMethod.FILE)) { - final File file = new File(uri); - if (!file.exists()) - throw new FileNotFoundException("unable to locate file: " + file); - sentResult = HTTPUtils.put(sbUrl.toString(), file, mimeType, gsuser, gspass); - } else if (method.equals(UploadMethod.EXTERNAL)) { - sentResult = HTTPUtils.put(sbUrl.toString(), uri.toString(), mimeType, gsuser, gspass); - } else if (method.equals(UploadMethod.URL)) { - // TODO check - sentResult = HTTPUtils.put(sbUrl.toString(), uri.toString(), mimeType, gsuser, gspass); - } - - if (sentResult != null) { - if (LOGGER.isInfoEnabled()) - LOGGER.info("Store successfully created using ( " + uri + " )"); - return true; - } else { - if (LOGGER.isErrorEnabled()) - LOGGER.error("Error in creating store using: " + uri); - return false; - } - - } - - /** - * Upload and publish vector data, and automatically create the associated DataStore if needed. - * - * Note that this is same as {@link #createStore} but specific for vector data (that is, {@code dsType} being {@link DataStoreType#DATASTORES}). - * - * @param workspace workspace where data will be published. - * @param storeName DataStore name. Will be created if doesn't exist. - * @param method Publication method. See {@link UploadMethod}. - * @param extension Dataset format. One of {@link DataStoreExtension}. - * @param mimeType Dataset MIME type. - * @param uri The data location. A local path for {@link UploadMethod#FILE}, a server path for {@link UploadMethod#EXTERNAL}, or a URL for - * {@link UploadMethod#URL}. - * @param configure Indicate if associated feature types will be automatically configured. See {@link ParameterConfigure}. - * @param params Other parameters to be included in request, such as "update" or "charset". See GeoServer REST documentation for details: dataStore operations. - * @return {@code true} if the store was successfully created. - * @throws FileNotFoundException if local file does not exist when using the {@link UploadMethod#FILE} method. - * @throws IllegalArgumentException if any of the mandatory {@code workspace}, {@code storeName}, {@code method}, {@code extension}, - * {@code mimeType} or {@code uri} parameters are {@code null}. - */ - private boolean createDataStore(String workspace, String storeName, UploadMethod method, - DataStoreExtension extension, String mimeType, URI uri, ParameterConfigure configure, - NameValuePair... params) throws FileNotFoundException, IllegalArgumentException { - return createStore(workspace, StoreType.DATASTORES, storeName, method, extension, mimeType, - uri, configure, params); - } - - /** - * Upload and publish raster data, and automatically create the associated CoverageStore if needed. - * - * Note that this is same as {@link #createStore} but specific for raster data (that is, {@code dsType} being {@link DataStoreType#COVERAGESTORES} - * ). - * - * @param workspace workspace where data will be published. - * @param storeName CoverageStore name. Will be created if doesn't exist. - * @param method Publication method. See {@link UploadMethod}. - * @param extension Dataset format. One of {@link CoverageStoreExtension}. - * @param mimeType Dataset MIME type. - * @param uri The data location. A local path for {@link UploadMethod#FILE}, a server path for {@link UploadMethod#EXTERNAL}, or a URL for - * {@link UploadMethod#URL}. - * @param configure Indicate if associated feature types will be automatically configured. See {@link ParameterConfigure}. - * @param params Other parameters to be included in request, such as "coverageName". See GeoServer REST documentation for details: coverageStore operations. - * @return {@code true} if the store was successfully created. - * @throws FileNotFoundException if local file does not exist when using the {@link UploadMethod#FILE} method. - * @throws IllegalArgumentException if any of the mandatory {@code workspace}, {@code storeName}, {@code method}, {@code extension}, - * {@code mimeType} or {@code uri} parameters are {@code null}. - */ - private boolean createCoverageStore(String workspace, String storeName, UploadMethod method, - CoverageStoreExtension extension, String mimeType, URI uri, - ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException, - IllegalArgumentException { - return createStore(workspace, StoreType.COVERAGESTORES, storeName, method, extension, - mimeType, uri, configure, params); - } - - /** - * Create a PostGIS datastore. - * - * @deprecated Will be deleted in next version 1.5.x, use {@link GeoServerRESTDatastoreManager} instead. - * - * @param workspace Name of the workspace to contain the database. This will also be the prefix of any layer names created from tables in the - * database. - * @param datastoreEncoder the set of parameters to be set to the datastore (including connection params). - * - * @return true if the PostGIS datastore has been successfully created, false otherwise - */ - public boolean createPostGISDatastore(String workspace, - GSPostGISDatastoreEncoder datastoreEncoder) { - String sUrl = restURL + "/rest/workspaces/" + workspace + "/datastores/"; - String xml = datastoreEncoder.toString(); - String result = HTTPUtils.postXml(sUrl, xml, gsuser, gspass); - return result != null; - } - - /** - * @deprecated Will be removed in the next release. - */ - public boolean publishDBLayer(String workspace, String storename, String layername, String srs, - String defaultStyle) { - - final GSFeatureTypeEncoder fte = new GSFeatureTypeEncoder(); - - fte.setProjectionPolicy(ProjectionPolicy.REPROJECT_TO_DECLARED); - fte.addKeyword("KEYWORD"); - fte.setTitle(layername); - fte.setName(layername); - fte.setSRS(srs); // srs=null?"EPSG:4326":srs); - final GSLayerEncoder layerEncoder = new GSLayerEncoder(); - layerEncoder.setDefaultStyle(defaultStyle); - return publishDBLayer(workspace, storename, fte, layerEncoder); - } - - /** - * Publish and configure a new layer from an existing DataStore (v. gr. a layer from a DB table). - * - * @param workspace Workspace name where DataStore is. - * @param storename DataStore name. - * @param fte FeatureType configuration details using a {@link GSFeatureTypeEncoder}. - * @return {@code true} if layer is successfully created. - */ - public boolean publishDBLayer(final String workspace, final String storename, - final GSFeatureTypeEncoder fte, final GSLayerEncoder layerEncoder) { - /* - * This is the equivalent call with cUrl: - * - * {@code curl -u admin:geoserver -XPOST -H 'Content-type: text/xml' \ -d - * "easia_gaul_1_aggrEPSG:4326true" \ - * http://localhost:8080/geoserver/rest/workspaces/it.geosolutions/ datastores/pg_kids/featuretypes } - * - * and a PUT to
restURL + "/rest/layers/" workspace + : + layerName - */ - String ftypeXml = fte.toString(); - StringBuilder postUrl = new StringBuilder(restURL).append("/rest/workspaces/") - .append(workspace).append("/datastores/").append(storename).append("/featuretypes"); - - final String layername = fte.getName(); - if (layername == null || layername.isEmpty()) { - if (LOGGER.isErrorEnabled()) - LOGGER.error("GSFeatureTypeEncoder has no valid name associated, try using GSFeatureTypeEncoder.setName(String)"); - return false; - } - - String configuredResult = HTTPUtils.postXml(postUrl.toString(), ftypeXml, this.gsuser, - this.gspass); - boolean published = configuredResult != null; - boolean configured = false; - - if (!published) { - LOGGER.warn("Error in publishing (" + configuredResult + ") " + workspace + ":" - + storename + "/" + layername); - } else { - LOGGER.info("DB layer successfully added (layer:" + layername + ")"); - - if (layerEncoder == null) { - if (LOGGER.isErrorEnabled()) - LOGGER.error("GSLayerEncoder is null: Unable to find the defaultStyle for this layer"); - return false; - } - - configured = configureLayer(workspace, layername, layerEncoder); - - if (!configured) { - LOGGER.warn("Error in configuring (" + configuredResult + ") " + workspace + ":" - + storename + "/" + layername); - } else { - LOGGER.info("DB layer successfully configured (layer:" + layername + ")"); - } - } - - return published && configured; - } - - // ========================================================================== - // === SHAPEFILES - // ========================================================================== - - /** - * Upload an publish a local shapefile. - *

- * The SRS will be set to EPSG:4326. - * - * @see {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, ProjectionPolicy, String)} - * - * @param workspace The workspace name. - * @param storename The store name. - * @param layername The layer name. - * @param zipFile The zipped file to publish. - * - * @return {@code true} if the operation completed successfully. - * @throws FileNotFoundException , IllegalArgumentException - */ - public boolean publishShp(String workspace, String storename, String datasetname, File zipFile) - throws FileNotFoundException, IllegalArgumentException { - return publishShp(workspace, storename, new NameValuePair[0], datasetname, - UploadMethod.FILE, zipFile.toURI(), DEFAULT_CRS, null); - } - - /** - * Publish a shapefile. - * - * @param workspace the name of the workspace to use - * @param storename the name of the store to create - * @param storeParams parameters to append to the url (can be null).
- * Accepted parameters are:
- *

- * @param layername the name of the layer to configure - * @param method {@link UploadMethod} - * @param fileUri the uri of the file containing the shapefile.It should be: - * - * @param srs the SRS for this shapefile. It must be an ESPG code or GeoServer will choke. - * @param nativeCRS the nativeCRS for this shapefile. It can be an EPSG code (for {@link ProjectionPolicy#NONE} or a WKT for - * {@link ProjectionPolicy#REPROJECT_TO_DECLARED}. - * @param policy {@link ProjectionPolicy} - * @param defaultStyle the default style to set (can be null). - * @return true if success false otherwise - * - * @throws FileNotFoundException if file to upload is not found - * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. - */ - public boolean publishShp(String workspace, String storeName, NameValuePair[] storeParams, - String datasetName, UploadMethod method, URI shapefile, String srs, String nativeCRS, - ProjectionPolicy policy, String defaultStyle) throws FileNotFoundException, - IllegalArgumentException { - if (workspace == null || storeName == null || shapefile == null || datasetName == null - || policy == null) { - throw new IllegalArgumentException("Unable to run: null parameter"); - } - - // - // SRS Policy Management - // - boolean srsNull = !(srs != null && srs.length() != 0); - boolean nativeSrsNull = !(nativeCRS != null && nativeCRS.length() != 0); - // if we are asking to use the reproject policy we must have the native crs - if (policy == ProjectionPolicy.REPROJECT_TO_DECLARED && (nativeSrsNull || srsNull)) { - throw new IllegalArgumentException( - "Unable to run: you can't ask GeoServer to reproject while not specifying a native CRS"); - } - - // if we are asking to use the NONE policy we must have the native crs. - if (policy == ProjectionPolicy.NONE && nativeSrsNull) { - throw new IllegalArgumentException( - "Unable to run: you can't ask GeoServer to use a native srs which is null"); - } - - // if we are asking to use the reproject policy we must have the native crs - if (policy == ProjectionPolicy.FORCE_DECLARED && srsNull) { - throw new IllegalArgumentException( - "Unable to run: you can't force GeoServer to use an srs which is null"); - } - - // - final String mimeType; - switch (method) { - case EXTERNAL: - case external: - mimeType = "text/plain"; - break; - case URL: // TODO check which mime-type should be used - case FILE: - case file: - case url: - mimeType = "application/zip"; - break; - default: - mimeType = null; - } - if (!createDataStore(workspace, - (storeName != null) ? storeName : FilenameUtils.getBaseName(shapefile.toString()), - method, DataStoreExtension.SHP, mimeType, shapefile, ParameterConfigure.NONE, - storeParams)) { - LOGGER.error("Unable to create data store for shapefile: " + shapefile); - return false; - } - - // config coverage props (srs) - final GSFeatureTypeEncoder featureTypeEncoder = new GSFeatureTypeEncoder(); - featureTypeEncoder.setName(datasetName); - featureTypeEncoder.setTitle(datasetName); - // set destination srs - if (!srsNull) { - featureTypeEncoder.setSRS(srs); - } else { - // this under the assumption that when the destination srs is null the nativeCRS has an EPSG one so we force them to be the same - featureTypeEncoder.setSRS(nativeCRS); - } - // set native srs - if (!nativeSrsNull) { - featureTypeEncoder.setNativeCRS(nativeCRS); - } - featureTypeEncoder.setProjectionPolicy(policy); - - if (!createResource(workspace, StoreType.DATASTORES, storeName, featureTypeEncoder)) { - LOGGER.error("Unable to create a coverage store for coverage: " + shapefile); - return false; - } - - // config layer props (style, ...) - final GSLayerEncoder layerEncoder = new GSLayerEncoder(); - if (defaultStyle != null && !defaultStyle.isEmpty()) - layerEncoder.setDefaultStyle(defaultStyle); - - return configureLayer(workspace, datasetName, layerEncoder); - } - - /** - * Publish a shapefile. - * - * @param workspace the name of the workspace to use - * @param storename the name of the store to create - * @param storeParams parameters to append to the url (can be null).
- * Accepted parameters are:
- * - * @param layername the name of the layer to configure - * @param method {@link UploadMethod} - * @param fileUri the uri of the file containing the shapefile.It should be: - * - * @param srs the SRS for this shapefile. It must be an ESPG code or GeoServer will choke. Notice that we can only use - * {@link ProjectionPolicy#FORCE_DECLARED}. - * @param policy {@link ProjectionPolicy} - * @param defaultStyle the default style to set (can be null). - * @return true if success false otherwise - * - * @throws FileNotFoundException if file to upload is not found - * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. - * @deprecated use {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, String)} instead as the behaviour of - * this method is misleading as it allows you to use wrong ProjectionPolicy values. - */ - public boolean publishShp(String workspace, String storeName, NameValuePair[] storeParams, - String datasetName, UploadMethod method, URI shapefile, String srs, - ProjectionPolicy policy, String defaultStyle) throws FileNotFoundException, - IllegalArgumentException { - return publishShp(workspace, storeName, storeParams, datasetName, method, shapefile, srs, - null, policy, defaultStyle); - } - - /** - * Publish a shapefile. - * - * @param workspace the name of the workspace to use - * @param storename the name of the store to create - * @param storeParams parameters to append to the url (can be null).
- * Accepted parameters are:
- * - * @param layername the name of the layer to configure - * @param method {@link UploadMethod} - * @param fileUri the uri of the file containing the shapefile.It should be: - * - * @param srs the SRS for this shapefile. It must be an ESPG code or GeoServer will choke. - * @param defaultStyle the default style to set (can be null). - * @return true if success false otherwise - * - * @throws FileNotFoundException if file to upload is not found - * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. - */ - public boolean publishShp(String workspace, String storeName, NameValuePair[] storeParams, - String datasetName, UploadMethod method, URI shapefile, String srs, String defaultStyle) - throws FileNotFoundException, IllegalArgumentException { - return publishShp(workspace, storeName, storeParams, datasetName, method, shapefile, srs, - null, ProjectionPolicy.FORCE_DECLARED, defaultStyle); - } - - /** - * Publish a zipped shapefile. - * - * @see {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, ProjectionPolicy, String)} - * - * @param workspace the name of the workspace to use - * @param storename the name of the store to create - * @param layerName the name of the layer to configure - * @param zipFile The zipped file to publish - * @param srs the srs for this shapefile. It will be forced to use this one in GeoServer using {@link ProjectionPolicy#FORCE_DECLARED}. - * @param defaultStyle the default style to set (can be null). - * - * @return {@code true} if the operation completed successfully - * @throws FileNotFoundException if file to upload is not found - * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. - */ - public boolean publishShp(String workspace, String storename, String layerName, File zipFile, - String srs, String defaultStyle) throws FileNotFoundException, IllegalArgumentException { - - return publishShp(workspace, storename, (NameValuePair[]) null, layerName, - UploadMethod.FILE, zipFile.toURI(), srs, defaultStyle); - } - - /** - * Publish a zipped shapefile forcing the srs to the one provided. - * - * @see {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, ProjectionPolicy, String)} - * - * @param workspace the name of the workspace to use - * @param storename the name of the store to create - * @param layername the name of the layer to configure - * @param zipFile The zipped file to publish - * @param srs the CRS for this shapefile. It must be an EPSG CODE ! - * - * @return {@code true} if the operation completed successfully. - * @throws FileNotFoundException if file to upload is not found - * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. - */ - public boolean publishShp(String workspace, String storename, String layername, File zipFile, - String srs) throws FileNotFoundException { - return publishShp(workspace, storename, (NameValuePair[]) null, layername, - UploadMethod.FILE, zipFile.toURI(), srs, null); - } - - /** - * Publish a zipped shapefile. - * - * @see {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, ProjectionPolicy, String)} - * - * @param workspace the name of the workspace to use - * @param storename the name of the store to create - * @param layername the name of the layer to configure - * @param zipFile the zip file containing the shapefile - * @param srs the shapefile srs. This must be an EPSG Codefor this code to work! - * @param params parameters to append to the url (can be null).
- * Accepted parameters are:
- * - * @return {@code true} if the operation completed successfully. - * @throws FileNotFoundException if file to upload is not found - * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. - */ - public boolean publishShp(String workspace, String storename, String layername, File zipFile, - String srs, NameValuePair... params) throws FileNotFoundException, - IllegalArgumentException { - - return publishShp(workspace, storename, params, layername, UploadMethod.FILE, - zipFile.toURI(), srs, null); - } - - /** - * Publish a collection of shapefiles. - *

- * Will automatically create the store and publish each shapefile as a layer. - * - * @param workspace the name of the workspace to use - * @param storeName the name of the store to create - * @param resource the shapefile collection. It can be: - *

- * @return {@code true} if publication successful. - * @throws FileNotFoundException if the specified zip file does not exist. - */ - public boolean publishShpCollection(String workspace, String storeName, URI resource) - throws FileNotFoundException { - - // Deduce upload method & mime type from resource syntax. - UploadMethod method = null; - String mime = null; - if (resource.getScheme().equals("file") || resource.isAbsolute() == false) { - File f = new File(resource); - if (f.exists() && f.isFile() && f.toString().endsWith(".zip")) { - method = UploadMethod.FILE; - mime = "application/zip"; - } else if (f.isDirectory()) { - method = UploadMethod.EXTERNAL; - mime = "text/plain"; - } - } else { - try { - if (resource.toURL() != null) { - method = UploadMethod.URL; - mime = "text/plain"; - } - } catch (MalformedURLException e) { - throw new IllegalArgumentException( - "Resource is not recognized as a zip file, or a directory, or a valid URL", - e); - } - } - - // Create store, upload data, and publish layers - return createStore(workspace, StoreType.DATASTORES, storeName, method, - DataStoreExtension.SHP, mime, resource, ParameterConfigure.ALL, - new NameValuePair[0]); - } - - // ========================================================================== - // === COVERAGES - // ========================================================================== - - /** - * Controls how existing data is handled when the file is PUT into a datastore that (a) already exists and (b) already contains a schema that - * matches the content of the file. It can take one of the two values: - * - * - * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it - */ - public static enum ParameterUpdate { - /** Data being uploaded is appended to the existing data. */ - APPEND, - /** Data being uploaded replaces any existing data. */ - OVERWRITE; - - /** - * Returns a lowercase representation of the parameter. Useful when constructing the REST request. - */ - @Override - public String toString() { - return this.name().toLowerCase(); - } - } - - /** - * Represents the format used to GET, PUT or POST information via REST. For example consider the resource "foo". To request a representation of - * foo as XML the request uri would end with "foo.xml". To request as JSON the request uri would end with "foo.json". When no format is specified - * the server will use its own internal format, usually html. - *

- * In a POST or PUT operation the format specifies 1) the representatin of the content being sent to the server, and 2) the representation of the - * response to be sent back. The former is specified with the Content-type header. To send a representation in XML, the content type "text/xml" or - * "application/xml" would be used. The latter is specified with the Accepts header as specified in the above paragraph describing a GET - * operation. - *

- * The following table defines the Content-type values for each format: - *

- */ - public enum Format { - XML, JSON, HTML, SLD; - - /** - * Gets the mime type from a format. - * - * @param f the format key. - * @return The content-type (mime), or {@code null} if not in the enum. - */ - public static String getContentType(Format f) { - switch (f) { - case XML: - return "application/xml"; - case HTML: - return "application/html"; - case JSON: - return "application/json"; - case SLD: - return "application/vnd.ogc.sld+xml"; - default: - return null; - } - } - - /** - * Gets the mime type from a format. - * - * @param f the format key. - * @return The content-type (mime), or {@code null} if not in the enum. - */ - public String getContentType() { - return getContentType(this); - } - - /** - * Returns a lowercase representation of the parameter. Useful when constructing the REST request. - */ - @Override - public String toString() { - return this.name().toLowerCase(); - } - } - - /** - * Upload and publish a raster file. - * - * @param workspace Workspace to use - * @param coveragestore Name of the coveragestore - * @param file file to upload - * @param configure Configure parameter. See {@link ParameterConfigure}. It can be null. - * @param params parameters to append to the url (can be null).
- * Accepted parameters are: - * - * @return true if the operation completed successfully. - */ - private boolean publishCoverage(String workspace, String coveragestore, - CoverageStoreExtension extension, String mimeType, File file, - ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException { - /* - * This is an example with cUrl: - * - * {@code curl -u admin:geoserver -XPUT -H 'Content-type: application/zip' \ --data-binary @$ZIPFILE \ http://$GSIP:$GSPORT/$SERVLET - * /rest/workspaces/$WORKSPACE/coveragestores /$COVERAGESTORE/file.worldimage - */ - return createCoverageStore(workspace, coveragestore, UploadMethod.FILE, extension, - mimeType, file.toURI(), configure, params); - } - - /** - * Publish a raster file local to the server. - * - * @param workspace Workspace to use - * @param coveragestore Name of the coveragestore - * @param file absolute path to the file location in the server - * @param configure Configure parameter. It may be null. - * @param update Accepted parameters are: - * - * @see #{@link ParameterConfigure} - * @return true if the operation completed successfully. - * @throws IllegalArgumentException - * @throws FileNotFoundException - */ - private boolean publishExternalCoverage(String workspace, String coveragestore, - CoverageStoreExtension extension, String mimeType, File file, - ParameterConfigure configure, ParameterUpdate update) throws FileNotFoundException, - IllegalArgumentException { - /* - * Curl example: - * - * {@code curl -u admin:geoserver -XPUT -H 'Content-type: application/zip' \ - * - * --data-binary @$ZIPFILE \ - * - * http://$GSIP:$GSPORT/$SERVLET/rest/workspaces/$WORKSPACE/coveragestores /$COVERAGESTORE/file.worldimage - */ - return createCoverageStore( - workspace, - coveragestore, - UploadMethod.EXTERNAL, - extension, - mimeType, - file.toURI(), - configure, - (update != null) ? new NameValuePair[] { new NameValuePair("update", update - .toString()) } : (NameValuePair[]) null); - } - - // ========================================================================== - // === GEOTIFF - // ========================================================================== - - /** - * Upload and publish a GeoTIFF image. - * - * @param workspace Workspace to use - * @param storeName The store name to be used or created. - * @param geotiff The GeoTIFF file. - * @return true if success. - * @throws FileNotFoundException if GeoTIFF file does not exist. - */ - public boolean publishGeoTIFF(String workspace, String storeName, File geotiff) - throws FileNotFoundException { - return publishCoverage(workspace, storeName, CoverageStoreExtension.GEOTIFF, - "image/geotiff", geotiff, ParameterConfigure.FIRST, (NameValuePair[]) null); - } - - /** - * Upload and publish a GeoTIFF image. - * - * @param workspace Workspace to use - * @param storeName Name of the coveragestore (if null the file name will be used) - * @param coverageName the name of the coverage (if null the file name will be used) - * @param geotiff file to upload - * @return true if the operation completed successfully. - * @throws FileNotFoundException if file does not exists - * @throws IllegalArgumentException if workspace or geotiff are null - */ - public boolean publishGeoTIFF(final String workspace, final String storeName, - final String coverageName, final File geotiff) throws FileNotFoundException, - IllegalArgumentException { - if (workspace == null || geotiff == null) - throw new IllegalArgumentException("Unable to proceed, some arguments are null"); - - return publishCoverage( - workspace, - (storeName != null) ? storeName : FilenameUtils.getBaseName(geotiff - .getAbsolutePath()), CoverageStoreExtension.GEOTIFF, "image/geotiff", - geotiff, ParameterConfigure.FIRST, - (coverageName != null) ? new NameValuePair[] { new NameValuePair("coverageName", - coverageName) } : (NameValuePair[]) null); - } - - /** - * Same as {@link #publishGeoTIFF(String, String, String, File, String, ProjectionPolicy, String, double[])} but without the last parameter - * (bbox). Kept here for backwards compatibility. - * - * @deprecated use the former method with bbox set to null. - */ - public boolean publishGeoTIFF(String workspace, String storeName, String resourceName, - File geotiff, String srs, ProjectionPolicy policy, String defaultStyle) - throws FileNotFoundException, IllegalArgumentException { - return publishGeoTIFF(workspace, storeName, resourceName, geotiff, srs, policy, - defaultStyle, null); - } - - /** - * Upload and publish a GeoTIFF image. - * - * @param workspace Workspace to use - * @param storeName Name of the coveragestore (if null the file name will be used) - * @param coverageName the name of the coverage (if null the file name will be used) - * @param geotiff file to upload - * @param srs the native CRS - * @param policy projection policy. See {@link ProjectionPolicy}. - * @param defaultStyle the default style to apply. - * @param bbox An array of 4 doubles indicating envelope in EPSG:4326. Order is [Xmin, Ymin, Xmax, Ymax]. - * @return true if the operation completed successfully. - * @throws FileNotFoundException if file does not exists - * @throws IllegalArgumentException if workspace or geotiff are null - * - */ - public boolean publishGeoTIFF(String workspace, String storeName, String coverageName, - File geotiff, String srs, ProjectionPolicy policy, String defaultStyle, double[] bbox) - throws FileNotFoundException, IllegalArgumentException { - if (workspace == null || storeName == null || geotiff == null || coverageName == null - || srs == null || policy == null || defaultStyle == null) - throw new IllegalArgumentException("Unable to run: null parameter"); - - if (!createCoverageStore( - workspace, - (storeName != null) ? storeName : FilenameUtils.getBaseName(geotiff - .getAbsolutePath()), UploadMethod.FILE, CoverageStoreExtension.GEOTIFF, - "image/geotiff", geotiff.toURI(), ParameterConfigure.NONE, (NameValuePair[]) null)) { - LOGGER.error("Unable to create coverage store for coverage: " + geotiff); - return false; - } - - // config coverage props (srs) - final GSCoverageEncoder coverageEncoder = new GSCoverageEncoder(); - coverageEncoder.setName(coverageName); - coverageEncoder.setTitle(coverageName); - coverageEncoder.setSRS(srs); - coverageEncoder.setProjectionPolicy(policy); - if (bbox != null && bbox.length == 4) { - coverageEncoder.setLatLonBoundingBox(bbox[0], bbox[1], bbox[2], bbox[3], DEFAULT_CRS); - } - - if (!createCoverage(workspace, storeName, coverageEncoder)) { - LOGGER.error("Unable to create a coverage store for coverage: " + geotiff); - return false; - } - - // config layer props (style, ...) - final GSLayerEncoder layerEncoder = new GSLayerEncoder(); - layerEncoder.setDefaultStyle(defaultStyle); - - return configureLayer(workspace, coverageName, layerEncoder); - } - - /** - * Publish a GeoTiff already in a filesystem readable by GeoServer. - * - * @param workspace an existing workspace - * @param storeName the coverageStore to be created - * @param geotiff the geoTiff to be published - * @param srs the native CRS - * @param policy projection policy. See {@link ProjectionPolicy}. - * @param defaultStyle the default style to apply. - * - * @return true if the operation completed successfully. - * @throws FileNotFoundException if file does not exists - * @throws IllegalArgumentException if any of the mandatory parameters are null. - */ - public boolean publishExternalGeoTIFF(String workspace, String storeName, File geotiff, - String coverageName, String srs, ProjectionPolicy policy, String defaultStyle) - throws FileNotFoundException, IllegalArgumentException { - if (workspace == null || storeName == null || geotiff == null || coverageName == null - || srs == null || policy == null || defaultStyle == null) - throw new IllegalArgumentException("Unable to run: null parameter"); - - // config coverage props (srs) - final GSCoverageEncoder coverageEncoder = new GSCoverageEncoder(); - coverageEncoder.setName(coverageName); - coverageEncoder.setTitle(coverageName); - coverageEncoder.setSRS(srs); - coverageEncoder.setProjectionPolicy(policy); - - // config layer props (style, ...) - final GSLayerEncoder layerEncoder = new GSLayerEncoder(); - layerEncoder.setDefaultStyle(defaultStyle); - - return publishExternalGeoTIFF(workspace, storeName, geotiff, coverageEncoder, layerEncoder) != null ? true - : false; - } - - /** - * Publish a GeoTiff already in a filesystem readable by GeoServer. - * - * @param workspace an existing workspace - * @param storeName the coverageStore to be created - * @param geotiff the geoTiff to be published - * @param coverageEncoder coverage details. See {@link GSCoverageEncoder}. - * @param layerEncoder layer details, See {@link GSLayerEncoder}. - * - * @return true if the operation completed successfully. - * @throws FileNotFoundException if file does not exists - * @throws IllegalArgumentException if any of the mandatory parameters are null. - */ - public RESTCoverageStore publishExternalGeoTIFF(final String workspace, final String storeName, - final File geotiff, final GSCoverageEncoder coverageEncoder, - final GSLayerEncoder layerEncoder) throws IllegalArgumentException, - FileNotFoundException { - - if (workspace == null || geotiff == null || storeName == null || layerEncoder == null - || coverageEncoder == null) - throw new IllegalArgumentException("Unable to run: null parameter"); - - final String coverageName = coverageEncoder.getName(); - if (coverageName.isEmpty()) { - throw new IllegalArgumentException("Unable to run: empty coverage store name"); - } - - // create store - final boolean store = publishExternalCoverage(workspace, storeName, - CoverageStoreExtension.GEOTIFF, "text/plain", geotiff, ParameterConfigure.NONE, - ParameterUpdate.OVERWRITE); - if (!store) { - return null; - } - - // create Coverage Store - if (!createCoverage(workspace, storeName, coverageEncoder)) { - if (LOGGER.isErrorEnabled()) - LOGGER.error("Unable to create a coverage for the store:" + coverageName); - return null; - } - - // create Layer - if (configureLayer(workspace, coverageName, layerEncoder)) { - GeoServerRESTReader reader; - try { - reader = new GeoServerRESTReader(this.restURL, this.gsuser, this.gspass); - return reader.getCoverageStore(workspace, storeName); - } catch (MalformedURLException e) { - LOGGER.error(e.getMessage(), e); - } - } - return null; - } - - // ========================================================================== - // === WORLDIMAGE - // ========================================================================== - - /** - * Publish a zipped worldimage file. It is assumed that the the zip-file contain the *.prj to set the srs. - * - * @param workspace Workspace to use - * @param coveragestore Name of the coveragestore - * @param zipFile zip file to upload - * - * @return true if the operation completed successfully. - */ - public boolean publishWorldImage(String workspace, String coveragestore, File zipFile) - throws FileNotFoundException { - return publishWorldImage(workspace, coveragestore, zipFile, ParameterConfigure.FIRST, - (NameValuePair) null); - } - - /** - * Publish a zipped worldimage file. It is assumed that the the zip-file contain the *.prj to set the srs. - * - * @param workspace Workspace to use - * @param coveragestore Name of the coveragestore - * @param zipFile zip file to upload - * @param configure Configure parameter. See {@link ParameterConfigure}. It can be null. - * @param params parameters to append to the url (can be null).
- * Accepted parameters are: - * - * @return true if the operation completed successfully. - */ - public boolean publishWorldImage(String workspace, String coveragestore, File zipFile, - ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException { - return publishCoverage(workspace, coveragestore, CoverageStoreExtension.WORLDIMAGE, - "application/zip", zipFile, configure, params); - } - - // ========================================================================== - // === MOSAIC - // ========================================================================== - - /** - * Publish imagemosaic as zip file. - * - * @param workspace Workspace to use - * @param storeName Name of the coveragestore - * @param zipFile file to upload - * - * @return true if the operation completed successfully. - */ - public boolean publishImageMosaic(String workspace, String storeName, File zipFile) - throws FileNotFoundException { - return publishCoverage(workspace, storeName, CoverageStoreExtension.IMAGEMOSAIC, - "application/zip", zipFile, ParameterConfigure.FIRST, (NameValuePair[]) null); - } - - /** - * Publish imagemosaic as zip file. - * - * @param workspace Workspace to use - * @param storeName Name of the coveragestore - * @param zipFile file to upload - * @param configure Configure parameter. See {@link ParameterConfigure}. It can be null. - * @param params parameters to append to the url (can be null).
- * Accepted parameters are: - * - * - * @return true if the operation completed successfully. - */ - public boolean publishImageMosaic(String workspace, String storeName, File zipFile, - ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException { - return publishCoverage(workspace, storeName, CoverageStoreExtension.IMAGEMOSAIC, - "application/zip", zipFile, configure, params); - } - - /** - * Publish a Mosaic from a filesystem currently readable by GeoServer. - * - * @param workspace an existing workspace - * @param storeName the name of the coverageStore to be created - * @param mosaicDir the directory where the raster images are located - * @param configure a specify if a coverage should be configured - * - * @return true if the operation completed successfully. - * @throws FileNotFoundException - */ - public RESTCoverageStore createExternaMosaicDatastore(String workspace, String storeName, - File mosaicDir, ParameterConfigure configure, ParameterUpdate update) - throws FileNotFoundException { - - /* - * Carlo (23 Nov 2011): commented out since this directory should be readable by target GeoServer not the calling client! - */ - if (!mosaicDir.isDirectory()) { - if (LOGGER.isWarnEnabled()) - LOGGER.warn("Directory '" - + mosaicDir - + "' not exists locally. Continue: please check existance on the remote server."); - } - - String sUrl = restURL + "/rest/workspaces/" + workspace + "/coveragestores/" + storeName - + "/external.imagemosaic?configure=" + configure.toString() + "&update=" - + update.toString(); - String sendResult = HTTPUtils.put(sUrl, mosaicDir.toURI().toString(), "text/plain", gsuser, - gspass); - return RESTCoverageStore.build(sendResult); - } - - /** - * Publish a Mosaic already in a filesystem readable by GeoServer. - * - * @param workspace an existing workspace - * @param storeName the name of the coverageStore to be created - * @param mosaicDir the directory where the raster images are located - * @param srs the coverage declared SRS - * @param defaultStyle may be null - * - * @return true if the operation completed successfully. - * - * @throws FileNotFoundException - */ - public boolean publishExternalMosaic(String workspace, String storeName, File mosaicDir, - String srs, String defaultStyle) throws FileNotFoundException { - - final GSCoverageEncoder coverageEncoder = new GSCoverageEncoder(); - coverageEncoder.setSRS(srs); - final String name = FilenameUtils.getBaseName(mosaicDir.getName()); - coverageEncoder.setName(name); - - final GSLayerEncoder layerEncoder = new GSLayerEncoder(); - layerEncoder.setDefaultStyle(defaultStyle); - - return publishExternalMosaic(workspace, storeName, mosaicDir, coverageEncoder, layerEncoder); - } - - /** - * @deprecated use {@link #publishExternalMosaic(String, String, File, GSCoverageEncoder, GSLayerEncoder)} - */ - public boolean createExternalMosaic(String workspace, String storeName, File mosaicDir, - GSCoverageEncoder coverageEncoder, GSLayerEncoder layerEncoder) - throws FileNotFoundException { - return publishExternalMosaic(workspace, storeName, mosaicDir, coverageEncoder, layerEncoder); - } - - /** - * Publish a Mosaic already in a filesystem readable by GeoServer. - * - * @param workspace an existing workspace - * @param storeName the name of the coverageStore to be created - * @param mosaicDir the directory where the raster images are located - * @param coverageEncoder the set of parameters to be set to the coverage (bbox, srs, ...) - * @param layerEncoder the set of parameters to be set to the layer (defaultstyle, wmspath, ...) - * - * @return true if the operation completed successfully. - * @throws FileNotFoundException - */ - public boolean publishExternalMosaic(String workspace, final String storeName, File mosaicDir, - GSCoverageEncoder coverageEncoder, GSLayerEncoder layerEncoder) - throws FileNotFoundException, IllegalArgumentException { - - if (coverageEncoder == null) { - throw new IllegalArgumentException("no coverageEncoder provided for mosaic " - + mosaicDir); - } - - if (layerEncoder == null) { - throw new IllegalArgumentException("no layerEncoder provided for " + mosaicDir); - } - - RESTCoverageStore store = createExternaMosaicDatastore(workspace, storeName, mosaicDir, - ParameterConfigure.NONE, ParameterUpdate.OVERWRITE); - - if (store == null) { - return false; - } - - // override name to match the FIRST configured coverage - String coverageName = coverageEncoder.getName(); - if (coverageName==null){ - coverageName=mosaicDir.getName(); - coverageEncoder.setName(coverageName); - } - if (!createCoverage(workspace, storeName, coverageEncoder)) { - if (LOGGER.isErrorEnabled()) - LOGGER.error("Unable to create a coverage for the store:" + coverageName); - return false; - } - if (!configureLayer(workspace, coverageName, layerEncoder)) { - if (LOGGER.isErrorEnabled()) - LOGGER.error("Unable to configure the Layer for the coverage:" + coverageName); - return false; - } - return true; - } - - // ========================================================================== - // === REMOVING THINGS - // ========================================================================== - - /** - * Remove the Coverage configuration from GeoServer. - *

- * First, the associated layer is removed, then the Coverage configuration itself. - *

- * CHECKME Maybe the coveragestore has to be removed as well. - * - * @return true if the operation completed successfully. - */ - public boolean unpublishCoverage(String workspace, String storename, String layerName) { - try { - final String fqLayerName; - - // this null check is here only for backward compatibility. - // workspace - // shall be mandatory. - if (workspace == null) { - - fqLayerName = layerName; - - if (LOGGER.isWarnEnabled()) { - LOGGER.warn("Null workspace while configuring layer : " + layerName - + " -- This behavior is deprecated."); - } - } else { - fqLayerName = workspace + ":" + layerName; - } - // delete related layer - URL deleteLayerUrl = new URL(restURL + "/rest/layers/" + fqLayerName); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Going to delete " + "/rest/layers/" + fqLayerName); - } - boolean layerDeleted = HTTPUtils - .delete(deleteLayerUrl.toExternalForm(), gsuser, gspass); - if (!layerDeleted) { - LOGGER.warn("Could not delete layer '" + fqLayerName + "'"); - return false; - } - // delete the coverage - URL deleteCovUrl = new URL(restURL + "/rest/workspaces/" + workspace - + "/coveragestores/" + storename + "/coverages/" + layerName); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Going to delete " + "/rest/workspaces/" + workspace - + "/coveragestores/" + storename + "/coverages/" + layerName); - } - boolean covDeleted = HTTPUtils.delete(deleteCovUrl.toExternalForm(), gsuser, gspass); - if (!covDeleted) { - LOGGER.warn("Could not delete coverage " + workspace + ":" + storename + "/" - + layerName + ", but layer was deleted."); - } else { - LOGGER.info("Coverage successfully deleted " + workspace + ":" + storename + "/" - + layerName); - } - return covDeleted; - - // the covstore is still there: should we delete it? - - } catch (MalformedURLException ex) { - if (LOGGER.isErrorEnabled()) - LOGGER.error(ex.getLocalizedMessage(), ex); - return false; - } - } - - /** - * Removes the featuretype and the associated layer. - *

- * You may also want to {@link #removeDatastore(String, String) remove the datastore}. - * - * @return true if the operation completed successfully. - */ - public boolean unpublishFeatureType(String workspace, String storename, String layerName) { - try { - - final String fqLayerName; - // this null check is here only for backward compatibility. - // workspace - // shall be mandatory. - if (workspace == null) { - - fqLayerName = layerName; - - if (LOGGER.isWarnEnabled()) { - LOGGER.warn("Null workspace while configuring layer : " + layerName - + " -- This behavior is deprecated."); - } - } else { - fqLayerName = workspace + ":" + layerName; - } - // delete related layer - URL deleteLayerUrl = new URL(restURL + "/rest/layers/" + fqLayerName); - boolean layerDeleted = HTTPUtils - .delete(deleteLayerUrl.toExternalForm(), gsuser, gspass); - if (!layerDeleted) { - LOGGER.warn("Could not delete layer '" + fqLayerName + "'"); - return false; - } - // delete the coverage - URL deleteFtUrl = new URL(restURL + "/rest/workspaces/" + workspace + "/datastores/" - + storename + "/featuretypes/" + layerName); - boolean ftDeleted = HTTPUtils.delete(deleteFtUrl.toExternalForm(), gsuser, gspass); - if (!ftDeleted) { - LOGGER.warn("Could not delete featuretype " + workspace + ":" + storename + "/" - + layerName + ", but layer was deleted."); - } else { - LOGGER.info("FeatureType successfully deleted " + workspace + ":" + storename + "/" - + layerName); - } - - return ftDeleted; - - // the store is still there: should we delete it? - - } catch (MalformedURLException ex) { - if (LOGGER.isErrorEnabled()) - LOGGER.error(ex.getLocalizedMessage(), ex); - return false; - } - } - - /** - * Remove recursively a given Datastore in a given Workspace. - * - * @param workspace The name of the workspace - * @param storename The name of the Datastore to remove. - * @return true if the datastore was successfully removed. - * - * @deprecated will be removed in next release use {@link GeoServerRESTPublisher#removeDatastore(String, String, boolean)} - */ - public boolean removeDatastore(String workspace, String storename) { - try { - return removeDatastore(workspace, storename, true); - } catch (IllegalArgumentException e) { - if (LOGGER.isErrorEnabled()) - LOGGER.error("Arguments may not be null or empty!", e); - } - return false; - } - - /** - * Remove a given Datastore in a given Workspace. - * - * @param workspace The name of the workspace - * @param storename The name of the Datastore to remove. - * @param recurse if remove should be performed recursively - * @throws IllegalArgumentException if workspace or storename are null or empty - * @return true if the datastore was successfully removed. - */ - public boolean removeDatastore(String workspace, String storename, final boolean recurse) - throws IllegalArgumentException { - return removeStore(workspace, storename, StoreType.DATASTORES, recurse); - } - - /** - * Remove recursively a given CoverageStore in a given Workspace. - * - * @param workspace The name of the workspace - * @param storename The name of the CoverageStore to remove. - * @return true if the CoverageStore was successfully removed. - * @deprecated use {@link #removeCoverageStore(String, String, boolean)} - */ - public boolean removeCoverageStore(String workspace, String storename) { - return removeCoverageStore(workspace, storename, true); - } - - /** - * Remove a given CoverageStore in a given Workspace. - * - * @param workspace The name of the workspace - * @param storename The name of the CoverageStore to remove. - * @param recurse if remove should be performed recursively - * @return true if the CoverageStore was successfully removed. - */ - public boolean removeCoverageStore(final String workspace, final String storename, - final boolean recurse) throws IllegalArgumentException { - return removeStore(workspace, storename, StoreType.COVERAGESTORES, recurse); - } - - /** - * Remove a given Datastore in a given Workspace. - * - * @param workspace The name of the workspace - * @param storename The name of the Datastore to remove. - * @param the {@link StoreType} type - * @param recurse if remove should be performed recursively - * @throws IllegalArgumentException if workspace or storename are null or empty - * @return true if the store was successfully removed. - */ - private boolean removeStore(String workspace, String storename, StoreType type, - final boolean recurse) throws IllegalArgumentException { - try { - if (workspace == null || storename == null) - throw new IllegalArgumentException("Arguments may not be null!"); - if (workspace.isEmpty() || storename.isEmpty()) - throw new IllegalArgumentException("Arguments may not be empty!"); - - final StringBuilder url = new StringBuilder(restURL); - url.append("/rest/workspaces/").append(workspace).append("/").append(type).append("/") - .append(storename); - if (recurse) - url.append("?recurse=true"); - final URL deleteStore = new URL(url.toString()); - - boolean deleted = HTTPUtils.delete(deleteStore.toExternalForm(), gsuser, gspass); - if (!deleted) { - LOGGER.warn("Could not delete store " + workspace + ":" + storename); - } else { - LOGGER.info("Store successfully deleted " + workspace + ":" + storename); - } - - return deleted; - } catch (MalformedURLException ex) { - if (LOGGER.isErrorEnabled()) - LOGGER.error(ex.getLocalizedMessage(), ex); - return false; - } - } - - /** - * Remove the workspace given Workspace using default parameters - * - * @see {@link GeoServerRESTPublisher#removeWorkspace(String, boolean)} - * @param workspace the workspace to remove - * @return true if success, false otherwise - * @deprecated {@link #removeWorkspace(String, boolean)} - */ - public boolean removeWorkspace(String workspace) { - return removeWorkspace(workspace, false); - } - - /** - * Remove a given Workspace. - * - * @param workspace The name of the workspace - * @param recurse The recurse parameter is used to recursively delete all resources contained by the specified workspace. This includes data - * stores, coverage stores, feature types, etc... Allowable values for this parameter are true or false. The default value is - * false. - * @return true if the WorkSpace was successfully removed. - */ - public boolean removeWorkspace(String workspace, boolean recurse) - throws IllegalArgumentException { - workspace = sanitize(workspace); - try { - if (workspace == null) - throw new IllegalArgumentException("Arguments may not be null!"); - if (workspace.isEmpty()) - throw new IllegalArgumentException("Arguments may not be empty!"); - - StringBuffer url = new StringBuffer(restURL).append("/rest/workspaces/").append( - workspace); - if (recurse) - url.append("?recurse=true"); - - deleteStylesForWorkspace(workspace); // !!! workaround - - final URL deleteUrl = new URL(url.toString()); - boolean deleted = HTTPUtils.delete(deleteUrl.toExternalForm(), gsuser, gspass); - if (!deleted) { - LOGGER.warn("Could not delete Workspace " + workspace); - } else { - LOGGER.info("Workspace successfully deleted " + workspace); - } - - return deleted; - } catch (MalformedURLException ex) { - if (LOGGER.isErrorEnabled()) - LOGGER.error(ex.getLocalizedMessage(), ex); - return false; - } - } - - /** - * workaround: geoserver does not delete styles inside workspaces - * https://jira.codehaus.org/browse/GEOS-5986 - */ - private void deleteStylesForWorkspace(String workspace) { - RESTStyleList styles = styleManager.getStyles(workspace); - if (styles==null) - return; - for (NameLinkElem nameLinkElem : styles) { - removeStyleInWorkspace(workspace, nameLinkElem.getName(), true); - } - } - - /** - * Remove a layer group. - * - * @param workspace the layer group workspace. - * @param name the layer group name. - * @return true if succeeded. - */ - public boolean removeLayerGroup(String workspace, String name) { - String url = restURL + "/rest"; - if (workspace == null) { - url += "/layergroups/" + name; - } else { - url += "/workspaces/" + workspace + "/layergroups/" + name; - } - - try { - URL deleteUrl = new URL(url); - boolean deleted = HTTPUtils.delete(deleteUrl.toExternalForm(), gsuser, gspass); - if (!deleted) { - if (LOGGER.isWarnEnabled()) - LOGGER.warn("Could not delete layergroup " + name); - } else { - if (LOGGER.isInfoEnabled()) - LOGGER.info("Layergroup successfully deleted: " + name); - } - - return deleted; - } catch (MalformedURLException ex) { - if (LOGGER.isErrorEnabled()) - LOGGER.error(ex.getLocalizedMessage(), ex); - return false; - } - } - - /** - * Remove a layer group. - * - * @param name the layer group name. - * @return true if succeeded. - */ - public boolean removeLayerGroup(String name) { - return removeLayerGroup(null, name); - } - - /** - * remove a generic given layer from a given workspace - * - * @param workspace - * @param layerName - * @return true if success - */ - public boolean removeLayer(final String workspace, final String layerName) { - - final String fqLayerName; - - // this null check is here only for backward compatibility. workspace - // shall be mandatory. - if (workspace == null) { - - fqLayerName = layerName; - - if (LOGGER.isWarnEnabled()) { - LOGGER.warn("Null workspace while removing layer : " + layerName - + " -- This behavior is deprecated."); - } - } else { - fqLayerName = workspace + ":" + layerName; - } - if (layerName == null) { - if (LOGGER.isErrorEnabled()) { - LOGGER.error("Null layerName : " + layerName); - } - return false; - } - - final String url = restURL + "/rest/layers/" + fqLayerName; - - boolean result = HTTPUtils.delete(url, gsuser, gspass); - if (result) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Layer successfully removed: " + fqLayerName); - } - } else { - if (LOGGER.isWarnEnabled()) - LOGGER.warn("Error removing layer " + fqLayerName); - } - - return result; - } - - // ========================================================================== - // === CATALOG REFRESHING - // ========================================================================== - - /** - * - * /workspaces//datastores/.xml /workspaces//coveragestores/.xml - * - * @param workspace - * @param storeName - * @param mosaicDir - * @return true if successfully reloaded - * @throws FileNotFoundException - * @throws IllegalArgumentException - * @throws MalformedURLException - */ - public boolean reloadStore(String workspace, final String storeName, StoreType storeType) - throws IllegalArgumentException, MalformedURLException { - final String url = HTTPUtils.append(this.restURL, "/rest/workspaces/", workspace, "/", - storeType.toString(), "/", storeName, ".xml").toString(); - final String store = HTTPUtils.get(url, this.gsuser, this.gspass); - - if (store != null) { - String storeTag = storeType.getTypeName(); - // switch (storeType) { - // case COVERAGESTORES: - // storeTag = storeType.toString().replaceAll("store", ""); - // break; - // case DATASTORES: - // storeTag = "featureTypes"; - // break; - // default: - // throw new IllegalArgumentException("Unrecognized type"); - // } - - String startTag = "<" + storeTag + ">"; - int start = store.indexOf(startTag); - String endTag = ""; - int stop = store.indexOf(endTag) + endTag.length(); - return HTTPUtils.putXml(url, store.subSequence(0, start) + store.substring(stop), - this.gsuser, this.gspass) != null ? true : false; - } else - return false; - } - - /** - * Reload the target geoserver configuration - * - * @return true if success - * - * @see GeoServer REST Config API - */ - public boolean reload() { - String sUrl = restURL + "/rest/reload"; - String result = HTTPUtils.post(sUrl, "", "text/plain", gsuser, gspass); - return result != null; - } - - /** - * Reset the target geoserver configuration - * - * @return true if success - * - * @see GeoServer REST Config API - */ - public boolean reset() { - String sUrl = restURL + "/rest/reset"; - String result = HTTPUtils.post(sUrl, "", "text/plain", gsuser, gspass); - return result != null; - } - - // ========================================================================== - // === MISCELLANEOUS - // ========================================================================== - - /** - * Allows to configure some layer attributes such as DefaultStyle - * - * @param workspace - * @param resourceName the name of the resource to use (featureStore or coverageStore name) - * @param layer the layer encoder used to configure the layer - * @return true if success - * @throws IllegalArgumentException if some arguments are null or empty - * - * @TODO WmsPath - */ - public boolean configureLayer(final String workspace, final String resourceName, - final GSLayerEncoder layer) throws IllegalArgumentException { - - if (workspace == null || resourceName == null || layer == null) { - throw new IllegalArgumentException("Null argument"); - } - // TODO: check this usecase, layer should always be defined - if (workspace.isEmpty() || resourceName.isEmpty() || layer.isEmpty()) { - throw new IllegalArgumentException("Empty argument"); - } - - final String fqLayerName = workspace + ":" + resourceName; - - final String url = restURL + "/rest/layers/" + fqLayerName; - - String layerXml = layer.toString(); - String sendResult = HTTPUtils.putXml(url, layerXml, gsuser, gspass); - if (sendResult != null) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Layer successfully configured: " + fqLayerName); - } - } else { - if (LOGGER.isWarnEnabled()) - LOGGER.warn("Error configuring layer " + fqLayerName + " (" + sendResult + ")"); - } - - return sendResult != null; - } - - /** - * Create a new LayerGroup using the specified encoder - * - * @param name name of the layer group - * @param group group encoder - * @return true if operation was successful - */ - public boolean createLayerGroup(String name, GSLayerGroupEncoder group) { - return createLayerGroup(null, name, group); - } - - /** - * Create a new LayerGroup using the specified encoder - * - * @param workspace name of the workspace - * @param name name of the layer group - * @param group group encoder - * @return true if operation was successful - */ - public boolean createLayerGroup(String workspace, String name, GSLayerGroupEncoder group) { - String url = restURL + "/rest"; - if (workspace == null) { - url += "/layergroups/"; - } else { - group.setWorkspace(workspace); - url += "/workspaces/" + workspace + "/layergroups/"; - } - - group.setName(name); - - String sendResult = HTTPUtils.postXml(url, group.toString(), gsuser, gspass); - if (sendResult != null) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("LayerGroup successfully configured: " + name); - } - } else { - if (LOGGER.isWarnEnabled()) - LOGGER.warn("Error configuring LayerGroup " + name + " (" + sendResult + ")"); - } - - return sendResult != null; - } - - /** - * Update a LayerGroup using the specified encoder - * - * @param name name of the layer group - * @param group group encoder - * @return true if operation was successful - */ - public boolean configureLayerGroup(String name, GSLayerGroupEncoder group) { - return configureLayerGroup(null, name, group); - } - - /** - * Update a LayerGroup using the specified encoder - * - * @param workspace name of the workspace - * @param name name of the layer group - * @param group group encoder - * @return true if operation was successful - */ - public boolean configureLayerGroup(String workspace, String name, GSLayerGroupEncoder group) { - String url = restURL + "/rest"; - if (workspace == null) { - url += "/layergroups/" + name; - } else { - url += "/workspaces/" + workspace + "/layergroups/" + name; - } - - String sendResult = HTTPUtils.putXml(url, group.toString(), gsuser, gspass); - if (sendResult != null) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("LayerGroup successfully configured: " + name); - } - } else { - if (LOGGER.isWarnEnabled()) - LOGGER.warn("Error configuring LayerGroup " + name + " (" + sendResult + ")"); - } - - return sendResult != null; - } - - /** - * Configure an existing coverage in a given workspace and coverage store - * - * @param ce contains the coverage name to configure and the configuration to apply - * @param wsname the workspace to search for existent coverage - * @param csname the coverage store to search for existent coverage - * @return true if success - */ - public boolean configureCoverage(final GSCoverageEncoder ce, final String wsname, - final String csname) { - return configureCoverage(ce, wsname, csname, ce.getName()); - } - - /** - * Configure an existing coverage in a given workspace and coverage store - * - * @param ce contains the coverage name to configure and the configuration to apply - * @param wsname the workspace to search for existent coverage - * @param csname the coverage store to search for existent coverage - * @param coverageName the name of the coverage, useful for changing name for the coverage itself - * @return true if success - */ - public boolean configureCoverage(final GSCoverageEncoder ce, final String wsname, - final String csname, final String coverageName) { - if (coverageName == null) { - if (LOGGER.isErrorEnabled()) - LOGGER.error("Unable to configure a coverage with no name try using GSCoverageEncoder.setName(String)"); - return false; - } - // retrieve coverage name - GeoServerRESTReader reader; - try { - reader = new GeoServerRESTReader(restURL, gsuser, gspass); - } catch (MalformedURLException e) { - if (LOGGER.isErrorEnabled()) - LOGGER.error(e.getLocalizedMessage(), e); - return false; - } - - // optimized search, left the old code for reference - RESTCoverage coverage = reader.getCoverage(wsname, csname, coverageName); -// final RESTCoverageList covList = reader.getCoverages(wsname, csname); -// if (covList==null||covList.isEmpty()) { -// if (LOGGER.isErrorEnabled()) -// LOGGER.error("No coverages found in new coveragestore " + csname); -// return false; -// } -// final Iterator it = covList.iterator(); -// while (it.hasNext()) { -// NameLinkElem nameElem = it.next(); -// if (nameElem.getName().equals(coverageName)) { -// found = true; -// break; -// } -// } - // if no coverage to configure is found return false - if (coverage==null) { - if (LOGGER.isErrorEnabled()) - LOGGER.error("No coverages found in new coveragestore " + csname + " called " - + coverageName); - return false; - } - - // configure the selected coverage - final String url = restURL + "/rest/workspaces/" + wsname + "/coveragestores/" + csname - + "/coverages/" + coverageName + ".xml"; - - final String xmlBody = ce.toString(); - final String sendResult = HTTPUtils.putXml(url, xmlBody, gsuser, gspass); - if (sendResult != null) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Coverage successfully configured " + wsname + ":" + csname + ":" - + coverageName); - } - } else { - if (LOGGER.isWarnEnabled()) - LOGGER.warn("Error configuring coverage " + wsname + ":" + csname + ":" + coverageName - + " (" + sendResult + ")"); - } - - return sendResult != null; - } - - /** - * @deprecated use {@link #createCoverage(String, String, GSCoverageEncoder)} - */ - public boolean createCoverage(final GSCoverageEncoder ce, final String wsname, - final String csname) { - return createCoverage(wsname, csname, ce); - } - - /** - * Create a new coverage in a given workspace and coverage store - * - * @param wsname the workspace to search for existent coverage - * @param storeName an existent store name to use as data source - * @param ce contains the coverage name to create and the configuration to apply - * @return true if success - * @throws IllegalArgumentException if arguments are null or empty - */ - public boolean createCoverage(final String wsname, final String storeName, - final GSCoverageEncoder ce) throws IllegalArgumentException { - return createResource(wsname, StoreType.COVERAGESTORES, storeName, ce); - } - - /** - * Create a new resource in a given workspace and store - * - * @param wsname the workspace to search for existent coverage - * @param storeName an existent store name to use as data source - * @param re contains the coverage name to create and the configuration to apply - * - * @TODO For FeatureType: The list parameter is used to control the category of feature types that are returned. It can take one of the three - * values configured, available, or all. - * - * configured - Only setup or configured feature types are returned. This is the default value. available - Only unconfigured feature types - * (not yet setup) but are available from the specified datastore will be returned. available_with_geom - Same as available but only - * includes feature types that have a geometry granule. all - The union of configured and available. - * - * - * @return true if success - * @throws IllegalArgumentException if arguments are null or empty - */ - private boolean createResource(String workspace, StoreType dsType, String storeName, - GSResourceEncoder re) throws IllegalArgumentException { - if (workspace == null || dsType == null || storeName == null || re == null) { - throw new IllegalArgumentException("Null argument"); - } - StringBuilder sbUrl = new StringBuilder(restURL).append("/rest/workspaces/") - .append(workspace).append("/").append(dsType).append("/").append(storeName) - .append("/").append(dsType.getTypeNameWithFormat(Format.XML)); - - final String resourceName = re.getName(); - if (resourceName == null) { - throw new IllegalArgumentException( - "Unable to configure a coverage using unnamed coverage encoder"); - } - - final String xmlBody = re.toString(); - final String sendResult = HTTPUtils.postXml(sbUrl.toString(), xmlBody, gsuser, gspass); - if (sendResult != null) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(dsType + " successfully created " + workspace + ":" + storeName + ":" - + resourceName); - } - } else { - if (LOGGER.isErrorEnabled()) - LOGGER.error("Error creating coverage " + workspace + ":" + storeName + ":" - + resourceName + " (" + sendResult + ")"); - } - - return sendResult != null; - } - - /** - * Appends ".DUMMY" to any string containing a dot (sic). - */ - protected String sanitize(String s) { - if (s.indexOf(".") != -1) { - return s + ".DUMMY"; - } - return s; - } - - /** - * Append params generating a string in the form: - *

- * NAME_0=VALUE_0&NAME_1=VALUE_1&....&NAME_n-1=VALUE_n-1 - *

- *
- * - * @param params an array of NameValuePair - * @return the parameter string or empty an string - */ - private String appendParameters(NameValuePair... params) { - StringBuilder sbUrl = new StringBuilder(); - // append parameters - if (params != null) { - final int paramsSize = params.length; - if (paramsSize > 0) { - int i = 0; - NameValuePair param = params[i]; - while (param != null && i++ < paramsSize) { - final String name = param.getName(); - final String value = param.getValue(); - // success - if (name != null && !name.isEmpty() && value != null && !value.isEmpty()) { - sbUrl.append(name).append("=").append(value); - // end cycle - param = null; - } else { - // next value - param = params[i]; - } - } - for (; i < paramsSize; i++) { - param = params[i]; - if (param != null) { - final String name = param.getName(); - final String value = param.getValue(); - sbUrl.append(name).append("=").append(value); - if (name != null && !name.isEmpty() && value != null && !value.isEmpty()) { - sbUrl.append("&").append(name).append("=").append(value); - } - - } - - } - } - } - return sbUrl.toString(); - } - - /** - * URL-encodes a String. - * - * @param s The original string. - * @return The encoded string. - */ - protected String encode(String s) { - // try { - // return URLEncoder.encode(s,"UTF-8"); - // } catch (UnsupportedEncodingException e) { - // LOGGER.warn("Error encoding :"+s+" with UTF-8: "+e.getLocalizedMessage()); - return URLEncoder.encode(s); - // } - } - - // ==> StructuredCoverageGridReader - - /** - * Create a store or harvest the coverage from the provided external path. - * - * @param workspace the GeoServer workspace - * @param coverageStore the GeoServer coverageStore - * @param format the format of the file to upload - * @param the absolut path to the file to upload - * - * @return true if the call succeeds or false otherwise. - */ - public boolean harvestExternal(String workspace, String coverageStore, String format, - String path) { - try { - GeoServerRESTStructuredGridCoverageReaderManager manager = new GeoServerRESTStructuredGridCoverageReaderManager( - new URL(restURL), gsuser, gspass); - return manager.harvestExternal(workspace, coverageStore, format, path); - } catch (IllegalArgumentException e) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info(e.getLocalizedMessage(), e); - } - } catch (MalformedURLException e) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info(e.getLocalizedMessage(), e); - } - } - return false; - } - - /** - * Create a new ImageMosaic with the provided configuration provided as a zip file. - * - *

- * This call configures all the coverages contained in the ImageMosaic. - * - * @param workspace the GeoServer workspace - * @param coverageStore the GeoServer coverageStore - * @param the absolute path to the file to upload - * - * @return true if the call succeeds or false otherwise. - * @since geoserver-2.4.0, geoserver-mng-1.6.0 - */ - public boolean createImageMosaic(String workspace, String coverageStore, String path) { - return createImageMosaic(workspace, coverageStore, path, ConfigureCoveragesOption.ALL); - } - - /** - * Create a new ImageMosaic with the provided configuration provided as a zip file. - * - *

- * With the options configure we can decide whether or not to configure or not the coverages contained in the ImageMosaic. - * - * @param workspace the GeoServer workspace - * @param coverageStore the GeoServer coverageStore - * @param the absolute path to the file to upload - * @param configureOpt tells GeoServer whether to configure all coverages in this mosaic (ALL) or none of them (NONE). - * - * @return true if the call succeeds or false otherwise. - * @since geoserver-2.4.0, geoserver-mng-1.6.0 - */ - public boolean createImageMosaic(String workspace, String coverageStore, String path, ConfigureCoveragesOption configureOpt) { - // checks - checkString(workspace); - checkString(coverageStore); - checkString(path); - final File zipFile= new File(path); - if(!zipFile.exists()||!zipFile.isFile()||!zipFile.canRead()){ - throw new IllegalArgumentException("The provided pathname does not point to a valide zip file: "+path); - } - // is it a zip? - ZipFile zip=null; - try{ - zip= new ZipFile(zipFile); - zip.getName(); - }catch (Exception e) { - LOGGER.trace(e.getLocalizedMessage(),e.getStackTrace()); - throw new IllegalArgumentException("The provided pathname does not point to a valide zip file: "+path); - }finally{ - if(zip!=null){ - try { - zip.close(); - } catch (IOException e) { - // swallow - LOGGER.trace(e.getLocalizedMessage(),e.getStackTrace()); - } - } - } - - // create URL - StringBuilder ss=HTTPUtils.append(restURL, "/rest/workspaces/", workspace, "/coveragestores/", - coverageStore, "/", UploadMethod.EXTERNAL.toString(), ".imagemosaic"); - switch(configureOpt){ - case ALL: - break; - case NONE: - ss.append("?configure=none"); - break; - default: - throw new IllegalArgumentException("Unrecognized COnfigureOption: "+configureOpt); - } - String sUrl = ss.toString(); - - // POST request - String result = HTTPUtils.put(sUrl, zipFile, "application/zip", gsuser, gspass); - return result != null; - } - - /** - * Remove a granule from a structured coverage by id. - * - * @param workspace the GeoServer workspace - * @param coverageStore the GeoServer coverageStore - * @param coverage the name of the target coverage from which we are going to remove - * @param filter the absolute path to the file to upload - * - * @return null in case the call does not succeed, or an instance of {@link RESTStructuredCoverageGranulesList}. - * - * @throws MalformedURLException - * @throws UnsupportedEncodingException - */ - public boolean removeGranuleById(final String workspace, String coverageStore, String coverage, - String granuleId) { - try { - GeoServerRESTStructuredGridCoverageReaderManager manager = new GeoServerRESTStructuredGridCoverageReaderManager( - new URL(restURL), gsuser, gspass); - return manager.removeGranuleById(workspace, coverageStore, coverage, granuleId); - } catch (IllegalArgumentException e) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info(e.getLocalizedMessage(), e); - } - } catch (MalformedURLException e) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info(e.getLocalizedMessage(), e); - } - } - return false; - } - - /** - * Remove granules from a structured coverage, by providing a CQL filter. - * - * @param workspace the GeoServer workspace - * @param coverageStore the GeoServer coverageStore - * @param coverage the name of the target coverage from which we are going to remove - * @param filter the absolute path to the file to upload - * - * @return null in case the call does not succeed, or an instance of {@link RESTStructuredCoverageGranulesList}. - * - * @throws MalformedURLException - * @throws UnsupportedEncodingException - */ - public boolean removeGranulesByCQL(final String workspace, String coverageStore, - String coverage, String filter) throws UnsupportedEncodingException { - try { - GeoServerRESTStructuredGridCoverageReaderManager manager = new GeoServerRESTStructuredGridCoverageReaderManager( - new URL(restURL), gsuser, gspass); - return manager.removeGranulesByCQL(workspace, coverageStore, coverage, filter); - } catch (IllegalArgumentException e) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info(e.getLocalizedMessage(), e); - } - } catch (MalformedURLException e) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info(e.getLocalizedMessage(), e); - } - } - return false; - - } - - /** - * Check the provided string for not being null or empty. - * - *

- * It throws an exception in case the string is either null or empty. - * - * @param string the {@link String} to be checked - */ - private static void checkString(String string) { - if (string == null) { - throw new NullPointerException("Provided string is is null!"); - } - if (string.length() <= 0) { - throw new IllegalArgumentException("Provided string is is empty!"); - } - - } - -} +/* + * GeoServer-Manager - Simple Manager Library for GeoServer + * + * Copyright (C) 2007,2013 GeoSolutions S.A.S. + * http://www.geo-solutions.it + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package it.geosolutions.geoserver.rest; + +import it.geosolutions.geoserver.rest.decoder.RESTCoverage; +import it.geosolutions.geoserver.rest.decoder.RESTCoverageStore; +import it.geosolutions.geoserver.rest.decoder.RESTStructuredCoverageGranulesList; +import it.geosolutions.geoserver.rest.decoder.RESTStyleList; +import it.geosolutions.geoserver.rest.decoder.utils.NameLinkElem; +import it.geosolutions.geoserver.rest.encoder.GSBackupEncoder; +import it.geosolutions.geoserver.rest.encoder.GSLayerEncoder; +import it.geosolutions.geoserver.rest.encoder.GSLayerGroupEncoder; +import it.geosolutions.geoserver.rest.encoder.GSNamespaceEncoder; +import it.geosolutions.geoserver.rest.encoder.GSPostGISDatastoreEncoder; +import it.geosolutions.geoserver.rest.encoder.GSResourceEncoder; +import it.geosolutions.geoserver.rest.encoder.GSResourceEncoder.ProjectionPolicy; +import it.geosolutions.geoserver.rest.encoder.GSWorkspaceEncoder; +import it.geosolutions.geoserver.rest.encoder.coverage.GSCoverageEncoder; +import it.geosolutions.geoserver.rest.encoder.feature.GSFeatureTypeEncoder; +import it.geosolutions.geoserver.rest.manager.GeoServerRESTStructuredGridCoverageReaderManager; +import it.geosolutions.geoserver.rest.manager.GeoServerRESTStructuredGridCoverageReaderManager.ConfigureCoveragesOption; +import it.geosolutions.geoserver.rest.manager.GeoServerRESTStyleManager; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLEncoder; +import java.util.zip.ZipFile; + +import org.apache.commons.httpclient.NameValuePair; +import org.apache.commons.io.FilenameUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Connect to a GeoServer instance to publish or modify its contents via REST API. + *

+ * There are no modifiable instance fields, so all the calls are thread-safe. + * + * @author ETj (etj at geo-solutions.it) + * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it + */ +public class GeoServerRESTPublisher { + + public static final String DEFAULT_CRS = "EPSG:4326"; + + /** The logger for this class */ + private static final Logger LOGGER = LoggerFactory.getLogger(GeoServerRESTPublisher.class); + + /** + * GeoServer instance base URL. E.g.: http://localhost:8080/geoserver. + */ + private final String restURL; + + /** + * GeoServer instance privileged username, with read & write permission on REST API + */ + private final String gsuser; + + /** + * GeoServer instance password for privileged username with r&w permission on REST API + */ + private final String gspass; + + + private final GeoServerRESTStyleManager styleManager; + /** + * Creates a GeoServerRESTPublisher to connect against a GeoServer instance with the given URL and user credentials. + * + * @param restURL the base GeoServer URL (e.g.: http://localhost:8080/geoserver) + * @param username auth credential + * @param password auth credential + */ + public GeoServerRESTPublisher(String restURL, String username, String password) { + this.restURL = HTTPUtils.decurtSlash(restURL); + this.gsuser = username; + this.gspass = password; + + URL url = null; + try { + url = new URL(restURL); + } catch (MalformedURLException ex) { + LOGGER.error("Bad URL: Calls to GeoServer are going to fail" , ex); + } + styleManager = new GeoServerRESTStyleManager(url, username, password); + } + + // ========================================================================== + // === BACKUP and RESTORE + // ========================================================================== + + /** + * Issues a GeoServer BACKUP. + *

+ * Won't include data, cached tiles, or logs. Use {@link #backup(String, boolean, boolean, boolean)} to control these parameters. + * + * @param backupDir the target Backup Dir String. + * + * @return id of the backup. + * @throws IllegalArgumentException if the backupDir is null or empty + */ + public String backup(final String backupDir) throws IllegalArgumentException { + /* + * This is the equivalent call with cUrl: + * + * {@code curl -u admin:geoserver -XPOST \ -H 'Content-type: text/xml' \ --data + * "<task><path>${BACKUP_DATADIR}</path></task>" \ ${restURL}/rest/bkprst/backup} + */ + return backup(backupDir, false, false, false); + } + + /** + * Issues a GeoServer BACKUP. + * + * @param backupDir the target Backup Dir String. + * @param includedata whether or not include the data dir Boolean. + * @param includegwc whether or not include the geowebcache dir Boolean. + * @param includelog whether or not include the log dir Boolean. + * + * @return id of the backup. + * @throws IllegalArgumentException if the backupDir is null or empty. + */ + public String backup(final String backupDir, final boolean includedata, + final boolean includegwc, final boolean includelog) throws IllegalArgumentException { + /* + * This is the equivalent call with cUrl: + * + * {@code curl -u admin:geoserver -XPOST \ -H 'Content-type: text/xml' \ --data + * "<task><path>${BACKUP_DATADIR}</path><includedata>${includedata}</includedata><includegwc>${includegwc}</includegwc><includelog>${includelog}</includelog></task>" + * \ ${restURL}/rest/bkprst/backup} + */ + if ((backupDir == null) || backupDir.isEmpty()) { + throw new IllegalArgumentException("The backup_dir must not be null or empty"); + } + + StringBuilder bkpUrl = new StringBuilder(restURL); + bkpUrl.append("/rest/bkprst/backup"); + + final GSBackupEncoder bkpenc = new GSBackupEncoder(backupDir); + bkpenc.setIncludeData(includedata); + bkpenc.setIncludeGwc(includegwc); + bkpenc.setIncludeLog(includelog); + final String result = HTTPUtils.post(bkpUrl.toString(), bkpenc.toString(), "text/xml", + gsuser, gspass); + + return result; + } + + /** + * Issues a GeoServer RESTORE. + * + * @param backupDir the source backup dir. + * + * @return id of the backup. + * @throws IllegalArgumentException if the backupDir is null or empty + */ + public String restore(final String backupDir) throws IllegalArgumentException { + /* + * This is the equivalent call with cUrl: + * + * {@code curl -u admin:geoserver -XPOST \ -H 'Content-type: text/xml' \ --data + * "<task><path>${BACKUP_DATADIR}</path></task>" \ ${restURL}/rest/bkprst/restore} + */ + if ((backupDir == null) || backupDir.isEmpty()) { + throw new IllegalArgumentException("The backup_dir must not be null or empty"); + } + + StringBuilder bkpUrl = new StringBuilder(restURL); + bkpUrl.append("/rest/bkprst/restore"); + + final GSBackupEncoder bkpenc = new GSBackupEncoder(backupDir); + + final String result = HTTPUtils.post(bkpUrl.toString(), bkpenc.toString(), "text/xml", + gsuser, gspass); + + return result; + } + + // ========================================================================== + // === WORKSPACES + // ========================================================================== + + /** + * Create a new Workspace. + *

+ * GeoServer will automatically create an associated Namespace with the URI being "http://{workspaceName}". To specify a custom Namespace URI, use + * {@link #createWorkspace(String, URI)}. + * + * @param workspace The name of the new workspace. + * + * @return true if the workspace was created. + */ + public boolean createWorkspace(final String workspace) { + /* + * This is the equivalent call with cUrl: + * + * {@code curl -u admin:geoserver -XPOST \ -H 'Content-type: text/xml' \ -d "$WORKSPACE" \ + * http://$GSIP:$GSPORT/$SERVLET/rest/workspaces } + */ + final String sUrl = restURL + "/rest/workspaces"; + final GSWorkspaceEncoder wsenc = new GSWorkspaceEncoder(workspace); + final String wsxml = wsenc.toString(); + final String result = HTTPUtils.postXml(sUrl, wsxml, gsuser, gspass); + return result != null; + } + + /** + * Create both a workspace and its associated namespace. + *

+ * Note that this method is equivalent to {@link #createNamespace}. + * + * @param name Name for the new workspace, which will be also its associated namespace prefix. + * @param uri Namespace URI. Cannot be empty. + * @return true if the Workspace and its associated namespace were successfully created. + */ + public boolean createWorkspace(final String name, final URI uri) { + // This is really an alias to createNamespace, as GeoServer + // will automatically create the associated workspace as well. + return createNamespace(name, uri); + } + + // ========================================================================== + // === NAMESPACES + // ========================================================================== + + /** + * Create a new Namespace. GeoServer will automatically create the corresponding Workspace. + * + * Prefix and URI are mandatory and cannot be empty. If a Namespace with the given prefix already exists, it won't be created. + * + * @param prefix The name of the new Namespace. + * @param uri The URI of the new Namespace. + * + * @return true if the Namespace was successfully created. + * @see GeoServer Documentation + */ + public boolean createNamespace(final String prefix, final URI uri) { + final String sUrl = restURL + "/rest/namespaces"; + final GSNamespaceEncoder nsenc = new GSNamespaceEncoder(prefix, uri); + final String nsxml = nsenc.toString(); + final String result = HTTPUtils.postXml(sUrl, nsxml, gsuser, gspass); + return result != null; + } + + /** + * Update a Namespace URI. + * + * Prefix and URI are mandatory and cannot be empty. A Namespace with the given prefix should exist. + * + * @param prefix The prefix of an existing Namespace. + * @param uri The new URI. + * + * @return true if the Namespace was successfully updated. + */ + public boolean updateNamespace(final String prefix, final URI uri) { + final String sUrl = restURL + "/rest/namespaces/" + encode(prefix); + final GSNamespaceEncoder nsenc = new GSNamespaceEncoder(prefix, uri); + final String nsxml = nsenc.toString(); + final String result = HTTPUtils.put(sUrl, nsxml, "application/xml", gsuser, gspass); + return result != null; + } + + /** + * Remove a given Namespace. It will remove the associated Workspace as well. + * + * @param prefix The Namespace prefix + * @param recurse The recurse parameter is used to recursively delete all resources contained in the workspace associated with this Namespace. + * This includes data stores, coverage stores, feature types, etc... Allowable values for this parameter are true or false. + * The default (safer) value is false. + * + * @return true if the Namespace was successfully removed. + */ + public boolean removeNamespace(final String prefix, boolean recurse) { + // Hack: We are instead calling removeWorkspace, as DELETE on + // a namespace will leave associated workspace in an inconsistent + // state. See https://jira.codehaus.org/browse/GEOS-5075 + // TODO switch to namespace when GEOS-5075 is solved + return removeWorkspace(prefix, recurse); + } + + // ========================================================================== + // === STYLES + // ========================================================================== + + /** + * Store and publish a Style. + * + * @param sldBody the full SLD document as a String. + * + * @return true if the operation completed successfully. + */ + public boolean publishStyle(String sldBody) { + return styleManager.publishStyle(sldBody); + } + + /** + * Store and publish a Style, assigning it a name. + * + * @param sldBody the full SLD document as a String. + * @param name the Style name. + * + * @return true if the operation completed successfully. + * @throws IllegalArgumentException if the style body is null or empty. + */ + public boolean publishStyle(final String sldBody, final String name) + throws IllegalArgumentException { + return styleManager.publishStyle(sldBody, name); + } + + /** + * Store and publish a Style. + * + * @param sldFile the File containing the SLD document. + * + * @return true if the operation completed successfully. + */ + public boolean publishStyle(File sldFile) { + return styleManager.publishStyle(sldFile); + } + + /** + * Store and publish a Style, assigning it a name. + * + * @param sldFile the File containing the SLD document. + * @param name the Style name. + * + * @return true if the operation completed successfully. + */ + public boolean publishStyle(File sldFile, String name) { + return styleManager.publishStyle(sldFile, name); + } + + /** + * Store and publish a Style, assigning it a name and choosing the raw format. + * + * @param sldFile the File containing the SLD document. + * @param name the Style name. + * @param raw the raw format + * + * @return true if the operation completed successfully. + */ + public boolean publishStyle(File sldFile, String name, boolean raw) { + return styleManager.publishStyle(sldFile, name, raw); + } + + /** + * Update a Style. + * + * @param sldBody the new SLD document as a String. + * @param name the Style name to update. + * + * @return true if the operation completed successfully. + * @throws IllegalArgumentException if the style body or name are null or empty. + */ + public boolean updateStyle(final String sldBody, final String name) + throws IllegalArgumentException { + return styleManager.updateStyle(sldBody, name); + } + + /** + * Update a Style. + * + * @param sldFile the File containing the SLD document. + * @param name the Style name. + * + * @return true if the operation completed successfully. + * @throws IllegalArgumentException if the sldFile file or name are null or name is empty. + */ + public boolean updateStyle(final File sldFile, final String name) + throws IllegalArgumentException { + + return styleManager.updateStyle(sldFile, name); + } + + /** + * Remove a Style. + *

+ * The Style will be unpublished, and (optionally) the SLD file will be removed. + * + * @param styleName the name of the Style to remove. + * @param purge remove the related SLD file from disk. + * + * @return true if the operation completed successfully. + * @throws IllegalArgumentException if styleName is null or empty. + */ + public boolean removeStyle(String styleName, final boolean purge) + throws IllegalArgumentException { + + return styleManager.removeStyle(styleName, purge); + } + + /** + * Remove a Style. + *

+ * The Style will be unpublished and the related SLD file will be removed. + * + * @param styleName the name of the Style to remove. + * + * @return true if the operation completed successfully. + */ + public boolean removeStyle(String styleName) { + return styleManager.removeStyle(styleName); + } + + /** + * @since GeoServer 2.2 + * @see GeoServerRESTStyleManager# + */ + public boolean publishStyleInWorkspace(String workspace, String sldBody) { + return styleManager.publishStyleInWorkspace(workspace, sldBody); + } + + /** + * @since GeoServer 2.2 + * @see GeoServerRESTStyleManager# + */ + public boolean publishStyleInWorkspace(String workspace, String sldBody, String name) throws IllegalArgumentException { + return styleManager.publishStyleInWorkspace(workspace, sldBody, name); + } + + /** + * @since GeoServer 2.2 + * @see GeoServerRESTStyleManager#publishStyleInWorkspace(java.lang.String, java.io.File) + */ + public boolean publishStyleInWorkspace(String workspace, File sldFile) { + return styleManager.publishStyleInWorkspace(workspace, sldFile); + } + + /** + * @since GeoServer 2.2 + * @see GeoServerRESTStyleManager#publishStyleInWorkspace(java.lang.String, java.io.File, java.lang.String) + */ + public boolean publishStyleInWorkspace(String workspace, File sldFile, String name) { + return styleManager.publishStyleInWorkspace(workspace, sldFile, name); + } + + /** + * @since GeoServer 2.2 + * @see GeoServerRESTStyleManager#updateStyleInWorkspace(java.lang.String, java.lang.String, java.lang.String) + */ + public boolean updateStyleInWorkspace(String workspace, String sldBody, String name) throws IllegalArgumentException { + return styleManager.updateStyleInWorkspace(workspace, sldBody, name); + } + + /** + * @since GeoServer 2.2 + * @see GeoServerRESTStyleManager#updateStyleInWorkspace(java.lang.String, java.io.File, java.lang.String) + */ + public boolean updateStyleInWorkspace(String workspace, File sldFile, String name) throws IllegalArgumentException { + return styleManager.updateStyleInWorkspace(workspace, sldFile, name); + } + + /** + * @since GeoServer 2.2 + * @see GeoServerRESTStyleManager#removeStyleInWorkspace(java.lang.String, java.lang.String, boolean) + */ + public boolean removeStyleInWorkspace(String workspace, String styleName, boolean purge) throws IllegalArgumentException { + return styleManager.removeStyleInWorkspace(workspace, styleName, purge); + } + + /** + * @since GeoServer 2.2 + * @see GeoServerRESTStyleManager#removeStyleInWorkspace(java.lang.String, java.lang.String) + */ + public boolean removeStyleInWorkspace(String workspace, String styleName) { + return styleManager.removeStyleInWorkspace(workspace, styleName); + } + + + // ========================================================================== + // === DATASTORE PUBLISHING + // ========================================================================== + + /** + * + * @author cancellieri + * @deprecated use {@link StoreType} + */ + public enum DataStoreType { + /** + * Raster based data sources. + */ + COVERAGESTORES, + /** + * Vector based data sources. Can be a file in the case of a Shapefile, a database connection in the case of PostGIS, or a server in the case + * of a remote Web Feature Service. + */ + DATASTORES; + + /** + * @deprecated use {@link StoreType#getTypeNameWithFormat(StoreType, Format)} + * @param type + * @return + */ + public static String getTypeName(StoreType type) { + return StoreType.getTypeNameWithFormat(type, Format.XML); + } + + /** + * @deprecated use {@link StoreType#toString()} + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + } + + /** + * DataStoreType definitions. + *

+ * + * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it + */ + public enum StoreType { + /** + * Raster based data sources. + */ + COVERAGESTORES, + /** + * Vector based data sources. Can be a file in the case of a Shapefile, a database connection in the case of PostGIS, or a server in the case + * of a remote Web Feature Service. + */ + DATASTORES; + + /** + * Get the type name of a StoreType with the specified format. + * + * @param type the StoreType. + * @param format see {@link Format} + * @return "featureTypes.{xml|html|...}" for DATASTORES, "coverages.{xml|html|...}" otherwise. + */ + public static String getTypeNameWithFormat(StoreType type, Format format) { + return getTypeName(type) + "." + format; + } + + /** + * Get the type name of a StoreType with the specified format. + * + * @param type the StoreType. + * @param format see {@link Format} + * @return "featuretypes.{xml|html|...}" for DATASTORES, "coverages.{xml|html|...}" otherwise. + */ + public String getTypeNameWithFormat(Format format) { + return getTypeName(this).toLowerCase() + "." + format; + } + + /** + * Get the type name of a StoreType. + * + * @param type the StoreType. + * @return "featureTypes" for DATASTORES, "coverages" otherwise. + */ + public static String getTypeName(StoreType type) { + switch (type) { + case COVERAGESTORES: + return "coverages"; // Format + case DATASTORES: + return "featureTypes"; + default: + return "coverages"; + } + } + + /** + * Get the type name of a StoreType. + * + * @param type the StoreType. + * @return "dataStore" for DATASTORES, "coverageStore" otherwise. + */ + public static String getType(StoreType type) { + switch (type) { + case COVERAGESTORES: + return "coverageStore"; // Format + case DATASTORES: + return "dataStore"; + default: + return "coverageStore"; + } + } + + /** + * Get the type name of a StoreType. + * + * @return "featuretypes" for DATASTORES, "coverages" otherwise. + */ + public String getTypeName() { + return getTypeName(this); + } + + /** + * Get the type of a StoreType. + * + * @param type the StoreType. + * @return "dataStore" for DATASTORES, "coverageStore" otherwise. + */ + public String getType() { + return getType(this); + } + + /** + * Returns a lowercase representation of the parameter value, suitable to construct the rest call. + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + } + + /** + * Specifies the method used to publish a resource. + * + * + * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it + */ + public enum UploadMethod { + /** + * directly upload a file from a local source. The body of the request is the file itself. + */ + FILE, @Deprecated + file, + /** + * indirectly upload a file from a remote source. The body of the request is the URL where the data is published. This url must be visible + * from the server. + */ + URL, @Deprecated + url, + /** + * forgo upload, and use an existing file on the server. The body of the request is the absolute local path to the existing file. + */ + EXTERNAL, @Deprecated + external; + + /** + * Returns a lowercase representation of the parameter value, suitable to construct the rest call. + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + } + + /** + * Vector data format being uploaded. Following extensions are supported: + * + * + * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it + */ + public enum DataStoreExtension { + /** Shapefile */ + SHP, + /** Properties file */ + PROPERTIES, + /** H2 Database */ + H2, + /** SpatiaLite Database */ + SPATIALITE; + + /** + * Returns a lowercase representation of the parameter value, suitable to construct the rest call. + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + } + + /** + * Raster data format being uploaded. Following extensions are supported: + * + * + * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it + */ + public enum CoverageStoreExtension { + /** GeoTiff coverage */ + GEOTIFF, + /** ImageMosaic */ + IMAGEMOSAIC, + /** Geo referenced image (JPEG,PNG,TIF) */ + WORLDIMAGE; + + /** + * Returns a lowercase representation of the parameter value, suitable to construct the rest call. + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + } + + /** + * Control if feature types are to be automatically configured upon file upload. It can take one of the three values: + * + */ + public static enum ParameterConfigure { + /** Configure first feature type only (default). */ + FIRST, + /** Don't configure any feature types. */ + NONE, + /** Configure all feature types. */ + ALL; + + /** + * Returns a lowercase representation of the parameter value, suitable to construct the rest call. + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + } + + /** + * Upload and publish data (vector or raster), and automatically create the associated Store if needed. + * + * This is a generic method; use {@link #createDataStore} and {@link #createCoverageStore} for vector and raster publishing respectively. + * + * @param workspace workspace where data will be published. + * @param dsType Store type. See {@link DataStoreType}. + * @param storeName Store name. Will be created if doesn't exist. + * @param method Publication method. See {@link UploadMethod}. + * @param extension Dataset format. One of {@link DataStoreExtension} for vector data, or one of {@link CoverageStoreExtension} for raster data. + * @param mimeType Dataset MIME type. + * @param uri The data location. A local path for {@link UploadMethod#FILE}, a server path for {@link UploadMethod#EXTERNAL}, or a URL for + * {@link UploadMethod#URL}. + * @param configure Indicate if associated feature types will be automatically configured. See {@link ParameterConfigure}. + * @param params Other parameters to be included in request, such as "update", "charset" or "coverageName". See GeoServer REST documentation for + * details: dataStore and coverageStore operations. + * @return {@code true} if the store was successfully created. + * @throws FileNotFoundException if local file does not exist when using the {@link UploadMethod#FILE} method. + * @throws IllegalArgumentException if any of the mandatory {@code workspace}, {@code dsType}, {@code storeName}, {@code method}, + * {@code extension}, {@code mimeType} or {@code uri} parameters are {@code null}. + */ + private boolean createStore(String workspace, StoreType dsType, String storeName, + UploadMethod method, Enum extension, String mimeType, URI uri, + ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException, + IllegalArgumentException { + if (workspace == null || dsType == null || storeName == null || method == null + || extension == null || mimeType == null || uri == null) { + throw new IllegalArgumentException("Null argument"); + } + StringBuilder sbUrl = new StringBuilder(restURL).append("/rest/workspaces/") + .append(workspace).append("/").append(dsType).append("/").append(storeName) + .append("/").append(method).append(".").append(extension); + + if (configure != null) { + sbUrl.append("?configure=").append(configure); + if (params != (NameValuePair[]) null) { + final String paramString = appendParameters(params); + if (!paramString.isEmpty()) { + sbUrl.append("&").append(paramString); + } + } + } + + String sentResult = null; + + if (method.equals(UploadMethod.FILE)) { + final File file = new File(uri); + if (!file.exists()) + throw new FileNotFoundException("unable to locate file: " + file); + sentResult = HTTPUtils.put(sbUrl.toString(), file, mimeType, gsuser, gspass); + } else if (method.equals(UploadMethod.EXTERNAL)) { + sentResult = HTTPUtils.put(sbUrl.toString(), uri.toString(), mimeType, gsuser, gspass); + } else if (method.equals(UploadMethod.URL)) { + // TODO check + sentResult = HTTPUtils.put(sbUrl.toString(), uri.toString(), mimeType, gsuser, gspass); + } + + if (sentResult != null) { + if (LOGGER.isInfoEnabled()) + LOGGER.info("Store successfully created using ( " + uri + " )"); + return true; + } else { + if (LOGGER.isErrorEnabled()) + LOGGER.error("Error in creating store using: " + uri); + return false; + } + + } + + /** + * Upload and publish vector data, and automatically create the associated DataStore if needed. + * + * Note that this is same as {@link #createStore} but specific for vector data (that is, {@code dsType} being {@link DataStoreType#DATASTORES}). + * + * @param workspace workspace where data will be published. + * @param storeName DataStore name. Will be created if doesn't exist. + * @param method Publication method. See {@link UploadMethod}. + * @param extension Dataset format. One of {@link DataStoreExtension}. + * @param mimeType Dataset MIME type. + * @param uri The data location. A local path for {@link UploadMethod#FILE}, a server path for {@link UploadMethod#EXTERNAL}, or a URL for + * {@link UploadMethod#URL}. + * @param configure Indicate if associated feature types will be automatically configured. See {@link ParameterConfigure}. + * @param params Other parameters to be included in request, such as "update" or "charset". See GeoServer REST documentation for details: dataStore operations. + * @return {@code true} if the store was successfully created. + * @throws FileNotFoundException if local file does not exist when using the {@link UploadMethod#FILE} method. + * @throws IllegalArgumentException if any of the mandatory {@code workspace}, {@code storeName}, {@code method}, {@code extension}, + * {@code mimeType} or {@code uri} parameters are {@code null}. + */ + private boolean createDataStore(String workspace, String storeName, UploadMethod method, + DataStoreExtension extension, String mimeType, URI uri, ParameterConfigure configure, + NameValuePair... params) throws FileNotFoundException, IllegalArgumentException { + return createStore(workspace, StoreType.DATASTORES, storeName, method, extension, mimeType, + uri, configure, params); + } + + /** + * Upload and publish raster data, and automatically create the associated CoverageStore if needed. + * + * Note that this is same as {@link #createStore} but specific for raster data (that is, {@code dsType} being {@link DataStoreType#COVERAGESTORES} + * ). + * + * @param workspace workspace where data will be published. + * @param storeName CoverageStore name. Will be created if doesn't exist. + * @param method Publication method. See {@link UploadMethod}. + * @param extension Dataset format. One of {@link CoverageStoreExtension}. + * @param mimeType Dataset MIME type. + * @param uri The data location. A local path for {@link UploadMethod#FILE}, a server path for {@link UploadMethod#EXTERNAL}, or a URL for + * {@link UploadMethod#URL}. + * @param configure Indicate if associated feature types will be automatically configured. See {@link ParameterConfigure}. + * @param params Other parameters to be included in request, such as "coverageName". See GeoServer REST documentation for details: coverageStore operations. + * @return {@code true} if the store was successfully created. + * @throws FileNotFoundException if local file does not exist when using the {@link UploadMethod#FILE} method. + * @throws IllegalArgumentException if any of the mandatory {@code workspace}, {@code storeName}, {@code method}, {@code extension}, + * {@code mimeType} or {@code uri} parameters are {@code null}. + */ + private boolean createCoverageStore(String workspace, String storeName, UploadMethod method, + CoverageStoreExtension extension, String mimeType, URI uri, + ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException, + IllegalArgumentException { + return createStore(workspace, StoreType.COVERAGESTORES, storeName, method, extension, + mimeType, uri, configure, params); + } + + /** + * Create a PostGIS datastore. + * + * @deprecated Will be deleted in next version 1.5.x, use {@link GeoServerRESTDatastoreManager} instead. + * + * @param workspace Name of the workspace to contain the database. This will also be the prefix of any layer names created from tables in the + * database. + * @param datastoreEncoder the set of parameters to be set to the datastore (including connection params). + * + * @return true if the PostGIS datastore has been successfully created, false otherwise + */ + public boolean createPostGISDatastore(String workspace, + GSPostGISDatastoreEncoder datastoreEncoder) { + String sUrl = restURL + "/rest/workspaces/" + workspace + "/datastores/"; + String xml = datastoreEncoder.toString(); + String result = HTTPUtils.postXml(sUrl, xml, gsuser, gspass); + return result != null; + } + + /** + * @deprecated Will be removed in the next release. + */ + public boolean publishDBLayer(String workspace, String storename, String layername, String srs, + String defaultStyle) { + + final GSFeatureTypeEncoder fte = new GSFeatureTypeEncoder(); + + fte.setProjectionPolicy(ProjectionPolicy.REPROJECT_TO_DECLARED); + fte.addKeyword("KEYWORD"); + fte.setTitle(layername); + fte.setName(layername); + fte.setSRS(srs); // srs=null?"EPSG:4326":srs); + final GSLayerEncoder layerEncoder = new GSLayerEncoder(); + layerEncoder.setDefaultStyle(defaultStyle); + return publishDBLayer(workspace, storename, fte, layerEncoder); + } + + /** + * Publish and configure a new layer from an existing DataStore (v. gr. a layer from a DB table). + * + * @param workspace Workspace name where DataStore is. + * @param storename DataStore name. + * @param fte FeatureType configuration details using a {@link GSFeatureTypeEncoder}. + * @return {@code true} if layer is successfully created. + */ + public boolean publishDBLayer(final String workspace, final String storename, + final GSFeatureTypeEncoder fte, final GSLayerEncoder layerEncoder) { + /* + * This is the equivalent call with cUrl: + * + * {@code curl -u admin:geoserver -XPOST -H 'Content-type: text/xml' \ -d + * "easia_gaul_1_aggrEPSG:4326true" \ + * http://localhost:8080/geoserver/rest/workspaces/it.geosolutions/ datastores/pg_kids/featuretypes } + * + * and a PUT to
restURL + "/rest/layers/" workspace + : + layerName + */ + String ftypeXml = fte.toString(); + StringBuilder postUrl = new StringBuilder(restURL).append("/rest/workspaces/") + .append(workspace).append("/datastores/").append(storename).append("/featuretypes"); + + final String layername = fte.getName(); + if (layername == null || layername.isEmpty()) { + if (LOGGER.isErrorEnabled()) + LOGGER.error("GSFeatureTypeEncoder has no valid name associated, try using GSFeatureTypeEncoder.setName(String)"); + return false; + } + + String configuredResult = HTTPUtils.postXml(postUrl.toString(), ftypeXml, this.gsuser, + this.gspass); + boolean published = configuredResult != null; + boolean configured = false; + + if (!published) { + LOGGER.warn("Error in publishing (" + configuredResult + ") " + workspace + ":" + + storename + "/" + layername); + } else { + LOGGER.info("DB layer successfully added (layer:" + layername + ")"); + + if (layerEncoder == null) { + if (LOGGER.isErrorEnabled()) + LOGGER.error("GSLayerEncoder is null: Unable to find the defaultStyle for this layer"); + return false; + } + + configured = configureLayer(workspace, layername, layerEncoder); + + if (!configured) { + LOGGER.warn("Error in configuring (" + configuredResult + ") " + workspace + ":" + + storename + "/" + layername); + } else { + LOGGER.info("DB layer successfully configured (layer:" + layername + ")"); + } + } + + return published && configured; + } + + // ========================================================================== + // === SHAPEFILES + // ========================================================================== + + /** + * Upload an publish a local shapefile. + *

+ * The SRS will be set to EPSG:4326. + * + * @see {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, ProjectionPolicy, String)} + * + * @param workspace The workspace name. + * @param storename The store name. + * @param layername The layer name. + * @param zipFile The zipped file to publish. + * + * @return {@code true} if the operation completed successfully. + * @throws FileNotFoundException , IllegalArgumentException + */ + public boolean publishShp(String workspace, String storename, String datasetname, File zipFile) + throws FileNotFoundException, IllegalArgumentException { + return publishShp(workspace, storename, new NameValuePair[0], datasetname, + UploadMethod.FILE, zipFile.toURI(), DEFAULT_CRS, null); + } + + /** + * Publish a shapefile. + * + * @param workspace the name of the workspace to use + * @param storename the name of the store to create + * @param storeParams parameters to append to the url (can be null).
+ * Accepted parameters are:
+ *

+ * @param layername the name of the layer to configure + * @param method {@link UploadMethod} + * @param fileUri the uri of the file containing the shapefile.It should be: + * + * @param srs the SRS for this shapefile. It must be an ESPG code or GeoServer will choke. + * @param nativeCRS the nativeCRS for this shapefile. It can be an EPSG code (for {@link ProjectionPolicy#NONE} or a WKT for + * {@link ProjectionPolicy#REPROJECT_TO_DECLARED}. + * @param policy {@link ProjectionPolicy} + * @param defaultStyle the default style to set (can be null). + * @return true if success false otherwise + * + * @throws FileNotFoundException if file to upload is not found + * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. + */ + public boolean publishShp(String workspace, String storeName, NameValuePair[] storeParams, + String datasetName, UploadMethod method, URI shapefile, String srs, String nativeCRS, + ProjectionPolicy policy, String defaultStyle) throws FileNotFoundException, + IllegalArgumentException { + if (workspace == null || storeName == null || shapefile == null || datasetName == null + || policy == null) { + throw new IllegalArgumentException("Unable to run: null parameter"); + } + + // + // SRS Policy Management + // + boolean srsNull = !(srs != null && srs.length() != 0); + boolean nativeSrsNull = !(nativeCRS != null && nativeCRS.length() != 0); + // if we are asking to use the reproject policy we must have the native crs + if (policy == ProjectionPolicy.REPROJECT_TO_DECLARED && (nativeSrsNull || srsNull)) { + throw new IllegalArgumentException( + "Unable to run: you can't ask GeoServer to reproject while not specifying a native CRS"); + } + + // if we are asking to use the NONE policy we must have the native crs. + if (policy == ProjectionPolicy.NONE && nativeSrsNull) { + throw new IllegalArgumentException( + "Unable to run: you can't ask GeoServer to use a native srs which is null"); + } + + // if we are asking to use the reproject policy we must have the native crs + if (policy == ProjectionPolicy.FORCE_DECLARED && srsNull) { + throw new IllegalArgumentException( + "Unable to run: you can't force GeoServer to use an srs which is null"); + } + + // + final String mimeType; + switch (method) { + case EXTERNAL: + case external: + mimeType = "text/plain"; + break; + case URL: // TODO check which mime-type should be used + case FILE: + case file: + case url: + mimeType = "application/zip"; + break; + default: + mimeType = null; + } + if (!createDataStore(workspace, + (storeName != null) ? storeName : FilenameUtils.getBaseName(shapefile.toString()), + method, DataStoreExtension.SHP, mimeType, shapefile, ParameterConfigure.NONE, + storeParams)) { + LOGGER.error("Unable to create data store for shapefile: " + shapefile); + return false; + } + + // config coverage props (srs) + final GSFeatureTypeEncoder featureTypeEncoder = new GSFeatureTypeEncoder(); + featureTypeEncoder.setName(datasetName); + featureTypeEncoder.setTitle(datasetName); + // set destination srs + if (!srsNull) { + featureTypeEncoder.setSRS(srs); + } else { + // this under the assumption that when the destination srs is null the nativeCRS has an EPSG one so we force them to be the same + featureTypeEncoder.setSRS(nativeCRS); + } + // set native srs + if (!nativeSrsNull) { + featureTypeEncoder.setNativeCRS(nativeCRS); + } + featureTypeEncoder.setProjectionPolicy(policy); + + if (!createResource(workspace, StoreType.DATASTORES, storeName, featureTypeEncoder)) { + LOGGER.error("Unable to create a coverage store for coverage: " + shapefile); + return false; + } + + // config layer props (style, ...) + final GSLayerEncoder layerEncoder = new GSLayerEncoder(); + if (defaultStyle != null && !defaultStyle.isEmpty()) + layerEncoder.setDefaultStyle(defaultStyle); + + return configureLayer(workspace, datasetName, layerEncoder); + } + + /** + * Publish a shapefile. + * + * @param workspace the name of the workspace to use + * @param storename the name of the store to create + * @param storeParams parameters to append to the url (can be null).
+ * Accepted parameters are:
+ * + * @param layername the name of the layer to configure + * @param method {@link UploadMethod} + * @param fileUri the uri of the file containing the shapefile.It should be: + * + * @param srs the SRS for this shapefile. It must be an ESPG code or GeoServer will choke. Notice that we can only use + * {@link ProjectionPolicy#FORCE_DECLARED}. + * @param policy {@link ProjectionPolicy} + * @param defaultStyle the default style to set (can be null). + * @return true if success false otherwise + * + * @throws FileNotFoundException if file to upload is not found + * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. + * @deprecated use {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, String)} instead as the behaviour of + * this method is misleading as it allows you to use wrong ProjectionPolicy values. + */ + public boolean publishShp(String workspace, String storeName, NameValuePair[] storeParams, + String datasetName, UploadMethod method, URI shapefile, String srs, + ProjectionPolicy policy, String defaultStyle) throws FileNotFoundException, + IllegalArgumentException { + return publishShp(workspace, storeName, storeParams, datasetName, method, shapefile, srs, + null, policy, defaultStyle); + } + + /** + * Publish a shapefile. + * + * @param workspace the name of the workspace to use + * @param storename the name of the store to create + * @param storeParams parameters to append to the url (can be null).
+ * Accepted parameters are:
+ * + * @param layername the name of the layer to configure + * @param method {@link UploadMethod} + * @param fileUri the uri of the file containing the shapefile.It should be: + * + * @param srs the SRS for this shapefile. It must be an ESPG code or GeoServer will choke. + * @param defaultStyle the default style to set (can be null). + * @return true if success false otherwise + * + * @throws FileNotFoundException if file to upload is not found + * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. + */ + public boolean publishShp(String workspace, String storeName, NameValuePair[] storeParams, + String datasetName, UploadMethod method, URI shapefile, String srs, String defaultStyle) + throws FileNotFoundException, IllegalArgumentException { + return publishShp(workspace, storeName, storeParams, datasetName, method, shapefile, srs, + null, ProjectionPolicy.FORCE_DECLARED, defaultStyle); + } + + /** + * Publish a zipped shapefile. + * + * @see {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, ProjectionPolicy, String)} + * + * @param workspace the name of the workspace to use + * @param storename the name of the store to create + * @param layerName the name of the layer to configure + * @param zipFile The zipped file to publish + * @param srs the srs for this shapefile. It will be forced to use this one in GeoServer using {@link ProjectionPolicy#FORCE_DECLARED}. + * @param defaultStyle the default style to set (can be null). + * + * @return {@code true} if the operation completed successfully + * @throws FileNotFoundException if file to upload is not found + * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. + */ + public boolean publishShp(String workspace, String storename, String layerName, File zipFile, + String srs, String defaultStyle) throws FileNotFoundException, IllegalArgumentException { + + return publishShp(workspace, storename, (NameValuePair[]) null, layerName, + UploadMethod.FILE, zipFile.toURI(), srs, defaultStyle); + } + + /** + * Publish a zipped shapefile forcing the srs to the one provided. + * + * @see {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, ProjectionPolicy, String)} + * + * @param workspace the name of the workspace to use + * @param storename the name of the store to create + * @param layername the name of the layer to configure + * @param zipFile The zipped file to publish + * @param srs the CRS for this shapefile. It must be an EPSG CODE ! + * + * @return {@code true} if the operation completed successfully. + * @throws FileNotFoundException if file to upload is not found + * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. + */ + public boolean publishShp(String workspace, String storename, String layername, File zipFile, + String srs) throws FileNotFoundException { + return publishShp(workspace, storename, (NameValuePair[]) null, layername, + UploadMethod.FILE, zipFile.toURI(), srs, null); + } + + /** + * Publish a zipped shapefile. + * + * @see {@link #publishShp(String, String, NameValuePair[], String, UploadMethod, URI, String, ProjectionPolicy, String)} + * + * @param workspace the name of the workspace to use + * @param storename the name of the store to create + * @param layername the name of the layer to configure + * @param zipFile the zip file containing the shapefile + * @param srs the shapefile srs. This must be an EPSG Codefor this code to work! + * @param params parameters to append to the url (can be null).
+ * Accepted parameters are:
+ * + * @return {@code true} if the operation completed successfully. + * @throws FileNotFoundException if file to upload is not found + * @throws IllegalArgumentException if any of the mandatory arguments are {@code null}. + */ + public boolean publishShp(String workspace, String storename, String layername, File zipFile, + String srs, NameValuePair... params) throws FileNotFoundException, + IllegalArgumentException { + + return publishShp(workspace, storename, params, layername, UploadMethod.FILE, + zipFile.toURI(), srs, null); + } + + /** + * Publish a collection of shapefiles. + *

+ * Will automatically create the store and publish each shapefile as a layer. + * + * @param workspace the name of the workspace to use + * @param storeName the name of the store to create + * @param resource the shapefile collection. It can be: + *

+ * @return {@code true} if publication successful. + * @throws FileNotFoundException if the specified zip file does not exist. + */ + public boolean publishShpCollection(String workspace, String storeName, URI resource) + throws FileNotFoundException { + + // Deduce upload method & mime type from resource syntax. + UploadMethod method = null; + String mime = null; + if (resource.getScheme().equals("file") || resource.isAbsolute() == false) { + File f = new File(resource); + if (f.exists() && f.isFile() && f.toString().endsWith(".zip")) { + method = UploadMethod.FILE; + mime = "application/zip"; + } else if (f.isDirectory()) { + method = UploadMethod.EXTERNAL; + mime = "text/plain"; + } + } else { + try { + if (resource.toURL() != null) { + method = UploadMethod.URL; + mime = "text/plain"; + } + } catch (MalformedURLException e) { + throw new IllegalArgumentException( + "Resource is not recognized as a zip file, or a directory, or a valid URL", + e); + } + } + + // Create store, upload data, and publish layers + return createStore(workspace, StoreType.DATASTORES, storeName, method, + DataStoreExtension.SHP, mime, resource, ParameterConfigure.ALL, + new NameValuePair[0]); + } + + // ========================================================================== + // === COVERAGES + // ========================================================================== + + /** + * Controls how existing data is handled when the file is PUT into a datastore that (a) already exists and (b) already contains a schema that + * matches the content of the file. It can take one of the two values: + * + * + * @author Carlo Cancellieri - carlo.cancellieri@geo-solutions.it + */ + public static enum ParameterUpdate { + /** Data being uploaded is appended to the existing data. */ + APPEND, + /** Data being uploaded replaces any existing data. */ + OVERWRITE; + + /** + * Returns a lowercase representation of the parameter. Useful when constructing the REST request. + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + } + + /** + * Represents the format used to GET, PUT or POST information via REST. For example consider the resource "foo". To request a representation of + * foo as XML the request uri would end with "foo.xml". To request as JSON the request uri would end with "foo.json". When no format is specified + * the server will use its own internal format, usually html. + *

+ * In a POST or PUT operation the format specifies 1) the representatin of the content being sent to the server, and 2) the representation of the + * response to be sent back. The former is specified with the Content-type header. To send a representation in XML, the content type "text/xml" or + * "application/xml" would be used. The latter is specified with the Accepts header as specified in the above paragraph describing a GET + * operation. + *

+ * The following table defines the Content-type values for each format: + *

+ */ + public enum Format { + XML, JSON, HTML, SLD; + + /** + * Gets the mime type from a format. + * + * @param f the format key. + * @return The content-type (mime), or {@code null} if not in the enum. + */ + public static String getContentType(Format f) { + switch (f) { + case XML: + return "application/xml"; + case HTML: + return "application/html"; + case JSON: + return "application/json"; + case SLD: + return "application/vnd.ogc.sld+xml"; + default: + return null; + } + } + + /** + * Gets the mime type from a format. + * + * @param f the format key. + * @return The content-type (mime), or {@code null} if not in the enum. + */ + public String getContentType() { + return getContentType(this); + } + + /** + * Returns a lowercase representation of the parameter. Useful when constructing the REST request. + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + } + + /** + * Upload and publish a raster file. + * + * @param workspace Workspace to use + * @param coveragestore Name of the coveragestore + * @param file file to upload + * @param configure Configure parameter. See {@link ParameterConfigure}. It can be null. + * @param params parameters to append to the url (can be null).
+ * Accepted parameters are: + * + * @return true if the operation completed successfully. + */ + private boolean publishCoverage(String workspace, String coveragestore, + CoverageStoreExtension extension, String mimeType, File file, + ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException { + /* + * This is an example with cUrl: + * + * {@code curl -u admin:geoserver -XPUT -H 'Content-type: application/zip' \ --data-binary @$ZIPFILE \ http://$GSIP:$GSPORT/$SERVLET + * /rest/workspaces/$WORKSPACE/coveragestores /$COVERAGESTORE/file.worldimage + */ + return createCoverageStore(workspace, coveragestore, UploadMethod.FILE, extension, + mimeType, file.toURI(), configure, params); + } + + /** + * Publish a raster file local to the server. + * + * @param workspace Workspace to use + * @param coveragestore Name of the coveragestore + * @param file absolute path to the file location in the server + * @param configure Configure parameter. It may be null. + * @param update Accepted parameters are: + * + * @see #{@link ParameterConfigure} + * @return true if the operation completed successfully. + * @throws IllegalArgumentException + * @throws FileNotFoundException + */ + private boolean publishExternalCoverage(String workspace, String coveragestore, + CoverageStoreExtension extension, String mimeType, File file, + ParameterConfigure configure, ParameterUpdate update) throws FileNotFoundException, + IllegalArgumentException { + /* + * Curl example: + * + * {@code curl -u admin:geoserver -XPUT -H 'Content-type: application/zip' \ + * + * --data-binary @$ZIPFILE \ + * + * http://$GSIP:$GSPORT/$SERVLET/rest/workspaces/$WORKSPACE/coveragestores /$COVERAGESTORE/file.worldimage + */ + return createCoverageStore( + workspace, + coveragestore, + UploadMethod.EXTERNAL, + extension, + mimeType, + file.toURI(), + configure, + (update != null) ? new NameValuePair[] { new NameValuePair("update", update + .toString()) } : (NameValuePair[]) null); + } + + // ========================================================================== + // === GEOTIFF + // ========================================================================== + + /** + * Upload and publish a GeoTIFF image. + * + * @param workspace Workspace to use + * @param storeName The store name to be used or created. + * @param geotiff The GeoTIFF file. + * @return true if success. + * @throws FileNotFoundException if GeoTIFF file does not exist. + */ + public boolean publishGeoTIFF(String workspace, String storeName, File geotiff) + throws FileNotFoundException { + return publishCoverage(workspace, storeName, CoverageStoreExtension.GEOTIFF, + "image/geotiff", geotiff, ParameterConfigure.FIRST, (NameValuePair[]) null); + } + + /** + * Upload and publish a GeoTIFF image. + * + * @param workspace Workspace to use + * @param storeName Name of the coveragestore (if null the file name will be used) + * @param coverageName the name of the coverage (if null the file name will be used) + * @param geotiff file to upload + * @return true if the operation completed successfully. + * @throws FileNotFoundException if file does not exists + * @throws IllegalArgumentException if workspace or geotiff are null + */ + public boolean publishGeoTIFF(final String workspace, final String storeName, + final String coverageName, final File geotiff) throws FileNotFoundException, + IllegalArgumentException { + if (workspace == null || geotiff == null) + throw new IllegalArgumentException("Unable to proceed, some arguments are null"); + + return publishCoverage( + workspace, + (storeName != null) ? storeName : FilenameUtils.getBaseName(geotiff + .getAbsolutePath()), CoverageStoreExtension.GEOTIFF, "image/geotiff", + geotiff, ParameterConfigure.FIRST, + (coverageName != null) ? new NameValuePair[] { new NameValuePair("coverageName", + coverageName) } : (NameValuePair[]) null); + } + + /** + * Same as {@link #publishGeoTIFF(String, String, String, File, String, ProjectionPolicy, String, double[])} but without the last parameter + * (bbox). Kept here for backwards compatibility. + * + * @deprecated use the former method with bbox set to null. + */ + public boolean publishGeoTIFF(String workspace, String storeName, String resourceName, + File geotiff, String srs, ProjectionPolicy policy, String defaultStyle) + throws FileNotFoundException, IllegalArgumentException { + return publishGeoTIFF(workspace, storeName, resourceName, geotiff, srs, policy, + defaultStyle, null); + } + + /** + * Upload and publish a GeoTIFF image. + * + * @param workspace Workspace to use + * @param storeName Name of the coveragestore (if null the file name will be used) + * @param coverageName the name of the coverage (if null the file name will be used) + * @param geotiff file to upload + * @param srs the native CRS + * @param policy projection policy. See {@link ProjectionPolicy}. + * @param defaultStyle the default style to apply. + * @param bbox An array of 4 doubles indicating envelope in EPSG:4326. Order is [Xmin, Ymin, Xmax, Ymax]. + * @return true if the operation completed successfully. + * @throws FileNotFoundException if file does not exists + * @throws IllegalArgumentException if workspace or geotiff are null + * + */ + public boolean publishGeoTIFF(String workspace, String storeName, String coverageName, + File geotiff, String srs, ProjectionPolicy policy, String defaultStyle, double[] bbox) + throws FileNotFoundException, IllegalArgumentException { + if (workspace == null || storeName == null || geotiff == null || coverageName == null + || srs == null || policy == null || defaultStyle == null) + throw new IllegalArgumentException("Unable to run: null parameter"); + + if (!createCoverageStore( + workspace, + (storeName != null) ? storeName : FilenameUtils.getBaseName(geotiff + .getAbsolutePath()), UploadMethod.FILE, CoverageStoreExtension.GEOTIFF, + "image/geotiff", geotiff.toURI(), ParameterConfigure.NONE, (NameValuePair[]) null)) { + LOGGER.error("Unable to create coverage store for coverage: " + geotiff); + return false; + } + + // config coverage props (srs) + final GSCoverageEncoder coverageEncoder = new GSCoverageEncoder(); + coverageEncoder.setName(coverageName); + coverageEncoder.setTitle(coverageName); + coverageEncoder.setSRS(srs); + coverageEncoder.setProjectionPolicy(policy); + if (bbox != null && bbox.length == 4) { + coverageEncoder.setLatLonBoundingBox(bbox[0], bbox[1], bbox[2], bbox[3], DEFAULT_CRS); + } + + if (!createCoverage(workspace, storeName, coverageEncoder)) { + LOGGER.error("Unable to create a coverage store for coverage: " + geotiff); + return false; + } + + // config layer props (style, ...) + final GSLayerEncoder layerEncoder = new GSLayerEncoder(); + layerEncoder.setDefaultStyle(defaultStyle); + + return configureLayer(workspace, coverageName, layerEncoder); + } + + /** + * Publish a GeoTiff already in a filesystem readable by GeoServer. + * + * @param workspace an existing workspace + * @param storeName the coverageStore to be created + * @param geotiff the geoTiff to be published + * @param srs the native CRS + * @param policy projection policy. See {@link ProjectionPolicy}. + * @param defaultStyle the default style to apply. + * + * @return true if the operation completed successfully. + * @throws FileNotFoundException if file does not exists + * @throws IllegalArgumentException if any of the mandatory parameters are null. + */ + public boolean publishExternalGeoTIFF(String workspace, String storeName, File geotiff, + String coverageName, String srs, ProjectionPolicy policy, String defaultStyle) + throws FileNotFoundException, IllegalArgumentException { + if (workspace == null || storeName == null || geotiff == null || coverageName == null + || srs == null || policy == null || defaultStyle == null) + throw new IllegalArgumentException("Unable to run: null parameter"); + + // config coverage props (srs) + final GSCoverageEncoder coverageEncoder = new GSCoverageEncoder(); + coverageEncoder.setName(coverageName); + coverageEncoder.setTitle(coverageName); + coverageEncoder.setSRS(srs); + coverageEncoder.setProjectionPolicy(policy); + + // config layer props (style, ...) + final GSLayerEncoder layerEncoder = new GSLayerEncoder(); + layerEncoder.setDefaultStyle(defaultStyle); + + return publishExternalGeoTIFF(workspace, storeName, geotiff, coverageEncoder, layerEncoder) != null ? true + : false; + } + + /** + * Publish a GeoTiff already in a filesystem readable by GeoServer. + * + * @param workspace an existing workspace + * @param storeName the coverageStore to be created + * @param geotiff the geoTiff to be published + * @param coverageEncoder coverage details. See {@link GSCoverageEncoder}. + * @param layerEncoder layer details, See {@link GSLayerEncoder}. + * + * @return true if the operation completed successfully. + * @throws FileNotFoundException if file does not exists + * @throws IllegalArgumentException if any of the mandatory parameters are null. + */ + public RESTCoverageStore publishExternalGeoTIFF(final String workspace, final String storeName, + final File geotiff, final GSCoverageEncoder coverageEncoder, + final GSLayerEncoder layerEncoder) throws IllegalArgumentException, + FileNotFoundException { + + if (workspace == null || geotiff == null || storeName == null || layerEncoder == null + || coverageEncoder == null) + throw new IllegalArgumentException("Unable to run: null parameter"); + + final String coverageName = coverageEncoder.getName(); + if (coverageName.isEmpty()) { + throw new IllegalArgumentException("Unable to run: empty coverage store name"); + } + + // create store + final boolean store = publishExternalCoverage(workspace, storeName, + CoverageStoreExtension.GEOTIFF, "text/plain", geotiff, ParameterConfigure.NONE, + ParameterUpdate.OVERWRITE); + if (!store) { + return null; + } + + // create Coverage Store + if (!createCoverage(workspace, storeName, coverageEncoder)) { + if (LOGGER.isErrorEnabled()) + LOGGER.error("Unable to create a coverage for the store:" + coverageName); + return null; + } + + // create Layer + if (configureLayer(workspace, coverageName, layerEncoder)) { + GeoServerRESTReader reader; + try { + reader = new GeoServerRESTReader(this.restURL, this.gsuser, this.gspass); + return reader.getCoverageStore(workspace, storeName); + } catch (MalformedURLException e) { + LOGGER.error(e.getMessage(), e); + } + } + return null; + } + + // ========================================================================== + // === WORLDIMAGE + // ========================================================================== + + /** + * Publish a zipped worldimage file. It is assumed that the the zip-file contain the *.prj to set the srs. + * + * @param workspace Workspace to use + * @param coveragestore Name of the coveragestore + * @param zipFile zip file to upload + * + * @return true if the operation completed successfully. + */ + public boolean publishWorldImage(String workspace, String coveragestore, File zipFile) + throws FileNotFoundException { + return publishWorldImage(workspace, coveragestore, zipFile, ParameterConfigure.FIRST, + (NameValuePair) null); + } + + /** + * Publish a zipped worldimage file. It is assumed that the the zip-file contain the *.prj to set the srs. + * + * @param workspace Workspace to use + * @param coveragestore Name of the coveragestore + * @param zipFile zip file to upload + * @param configure Configure parameter. See {@link ParameterConfigure}. It can be null. + * @param params parameters to append to the url (can be null).
+ * Accepted parameters are: + * + * @return true if the operation completed successfully. + */ + public boolean publishWorldImage(String workspace, String coveragestore, File zipFile, + ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException { + return publishCoverage(workspace, coveragestore, CoverageStoreExtension.WORLDIMAGE, + "application/zip", zipFile, configure, params); + } + + // ========================================================================== + // === MOSAIC + // ========================================================================== + + /** + * Publish imagemosaic as zip file. + * + * @param workspace Workspace to use + * @param storeName Name of the coveragestore + * @param zipFile file to upload + * + * @return true if the operation completed successfully. + */ + public boolean publishImageMosaic(String workspace, String storeName, File zipFile) + throws FileNotFoundException { + return publishCoverage(workspace, storeName, CoverageStoreExtension.IMAGEMOSAIC, + "application/zip", zipFile, ParameterConfigure.FIRST, (NameValuePair[]) null); + } + + /** + * Publish imagemosaic as zip file. + * + * @param workspace Workspace to use + * @param storeName Name of the coveragestore + * @param zipFile file to upload + * @param configure Configure parameter. See {@link ParameterConfigure}. It can be null. + * @param params parameters to append to the url (can be null).
+ * Accepted parameters are: + * + * + * @return true if the operation completed successfully. + */ + public boolean publishImageMosaic(String workspace, String storeName, File zipFile, + ParameterConfigure configure, NameValuePair... params) throws FileNotFoundException { + return publishCoverage(workspace, storeName, CoverageStoreExtension.IMAGEMOSAIC, + "application/zip", zipFile, configure, params); + } + + /** + * Publish a Mosaic from a filesystem currently readable by GeoServer. + * + * @param workspace an existing workspace + * @param storeName the name of the coverageStore to be created + * @param mosaicDir the directory where the raster images are located + * @param configure a specify if a coverage should be configured + * + * @return true if the operation completed successfully. + * @throws FileNotFoundException + */ + public RESTCoverageStore createExternaMosaicDatastore(String workspace, String storeName, + File mosaicDir, ParameterConfigure configure, ParameterUpdate update) + throws FileNotFoundException { + + /* + * Carlo (23 Nov 2011): commented out since this directory should be readable by target GeoServer not the calling client! + */ + if (!mosaicDir.isDirectory()) { + if (LOGGER.isWarnEnabled()) + LOGGER.warn("Directory '" + + mosaicDir + + "' not exists locally. Continue: please check existance on the remote server."); + } + + String sUrl = restURL + "/rest/workspaces/" + workspace + "/coveragestores/" + storeName + + "/external.imagemosaic?configure=" + configure.toString() + "&update=" + + update.toString(); + String sendResult = HTTPUtils.put(sUrl, mosaicDir.toURI().toString(), "text/plain", gsuser, + gspass); + return RESTCoverageStore.build(sendResult); + } + + /** + * Publish a Mosaic already in a filesystem readable by GeoServer. + * + * @param workspace an existing workspace + * @param storeName the name of the coverageStore to be created + * @param mosaicDir the directory where the raster images are located + * @param srs the coverage declared SRS + * @param defaultStyle may be null + * + * @return true if the operation completed successfully. + * + * @throws FileNotFoundException + */ + public boolean publishExternalMosaic(String workspace, String storeName, File mosaicDir, + String srs, String defaultStyle) throws FileNotFoundException { + + final GSCoverageEncoder coverageEncoder = new GSCoverageEncoder(); + coverageEncoder.setSRS(srs); + final String name = FilenameUtils.getBaseName(mosaicDir.getName()); + coverageEncoder.setName(name); + + final GSLayerEncoder layerEncoder = new GSLayerEncoder(); + layerEncoder.setDefaultStyle(defaultStyle); + + return publishExternalMosaic(workspace, storeName, mosaicDir, coverageEncoder, layerEncoder); + } + + /** + * @deprecated use {@link #publishExternalMosaic(String, String, File, GSCoverageEncoder, GSLayerEncoder)} + */ + public boolean createExternalMosaic(String workspace, String storeName, File mosaicDir, + GSCoverageEncoder coverageEncoder, GSLayerEncoder layerEncoder) + throws FileNotFoundException { + return publishExternalMosaic(workspace, storeName, mosaicDir, coverageEncoder, layerEncoder); + } + + /** + * Publish a Mosaic already in a filesystem readable by GeoServer. + * + * @param workspace an existing workspace + * @param storeName the name of the coverageStore to be created + * @param mosaicDir the directory where the raster images are located + * @param coverageEncoder the set of parameters to be set to the coverage (bbox, srs, ...) + * @param layerEncoder the set of parameters to be set to the layer (defaultstyle, wmspath, ...) + * + * @return true if the operation completed successfully. + * @throws FileNotFoundException + */ + public boolean publishExternalMosaic(String workspace, final String storeName, File mosaicDir, + GSCoverageEncoder coverageEncoder, GSLayerEncoder layerEncoder) + throws FileNotFoundException, IllegalArgumentException { + + if (coverageEncoder == null) { + throw new IllegalArgumentException("no coverageEncoder provided for mosaic " + + mosaicDir); + } + + if (layerEncoder == null) { + throw new IllegalArgumentException("no layerEncoder provided for " + mosaicDir); + } + + RESTCoverageStore store = createExternaMosaicDatastore(workspace, storeName, mosaicDir, + ParameterConfigure.NONE, ParameterUpdate.OVERWRITE); + + if (store == null) { + return false; + } + + // override name to match the FIRST configured coverage + String coverageName = coverageEncoder.getName(); + if (coverageName==null){ + coverageName=mosaicDir.getName(); + coverageEncoder.setName(coverageName); + } + if (!createCoverage(workspace, storeName, coverageEncoder)) { + if (LOGGER.isErrorEnabled()) + LOGGER.error("Unable to create a coverage for the store:" + coverageName); + return false; + } + if (!configureLayer(workspace, coverageName, layerEncoder)) { + if (LOGGER.isErrorEnabled()) + LOGGER.error("Unable to configure the Layer for the coverage:" + coverageName); + return false; + } + return true; + } + + // ========================================================================== + // === REMOVING THINGS + // ========================================================================== + + /** + * Remove the Coverage configuration from GeoServer. + *

+ * First, the associated layer is removed, then the Coverage configuration itself. + *

+ * CHECKME Maybe the coveragestore has to be removed as well. + * + * @return true if the operation completed successfully. + */ + public boolean unpublishCoverage(String workspace, String storename, String layerName) { + try { + final String fqLayerName; + + // this null check is here only for backward compatibility. + // workspace + // shall be mandatory. + if (workspace == null) { + + fqLayerName = layerName; + + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Null workspace while configuring layer : " + layerName + + " -- This behavior is deprecated."); + } + } else { + fqLayerName = workspace + ":" + layerName; + } + // delete related layer + URL deleteLayerUrl = new URL(restURL + "/rest/layers/" + fqLayerName); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Going to delete " + "/rest/layers/" + fqLayerName); + } + boolean layerDeleted = HTTPUtils + .delete(deleteLayerUrl.toExternalForm(), gsuser, gspass); + if (!layerDeleted) { + LOGGER.warn("Could not delete layer '" + fqLayerName + "'"); + return false; + } + // delete the coverage + URL deleteCovUrl = new URL(restURL + "/rest/workspaces/" + workspace + + "/coveragestores/" + storename + "/coverages/" + layerName); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Going to delete " + "/rest/workspaces/" + workspace + + "/coveragestores/" + storename + "/coverages/" + layerName); + } + boolean covDeleted = HTTPUtils.delete(deleteCovUrl.toExternalForm(), gsuser, gspass); + if (!covDeleted) { + LOGGER.warn("Could not delete coverage " + workspace + ":" + storename + "/" + + layerName + ", but layer was deleted."); + } else { + LOGGER.info("Coverage successfully deleted " + workspace + ":" + storename + "/" + + layerName); + } + return covDeleted; + + // the covstore is still there: should we delete it? + + } catch (MalformedURLException ex) { + if (LOGGER.isErrorEnabled()) + LOGGER.error(ex.getLocalizedMessage(), ex); + return false; + } + } + + /** + * Removes the featuretype and the associated layer. + *

+ * You may also want to {@link #removeDatastore(String, String) remove the datastore}. + * + * @return true if the operation completed successfully. + */ + public boolean unpublishFeatureType(String workspace, String storename, String layerName) { + try { + + final String fqLayerName; + // this null check is here only for backward compatibility. + // workspace + // shall be mandatory. + if (workspace == null) { + + fqLayerName = layerName; + + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Null workspace while configuring layer : " + layerName + + " -- This behavior is deprecated."); + } + } else { + fqLayerName = workspace + ":" + layerName; + } + // delete related layer + URL deleteLayerUrl = new URL(restURL + "/rest/layers/" + fqLayerName); + boolean layerDeleted = HTTPUtils + .delete(deleteLayerUrl.toExternalForm(), gsuser, gspass); + if (!layerDeleted) { + LOGGER.warn("Could not delete layer '" + fqLayerName + "'"); + return false; + } + // delete the coverage + URL deleteFtUrl = new URL(restURL + "/rest/workspaces/" + workspace + "/datastores/" + + storename + "/featuretypes/" + layerName); + boolean ftDeleted = HTTPUtils.delete(deleteFtUrl.toExternalForm(), gsuser, gspass); + if (!ftDeleted) { + LOGGER.warn("Could not delete featuretype " + workspace + ":" + storename + "/" + + layerName + ", but layer was deleted."); + } else { + LOGGER.info("FeatureType successfully deleted " + workspace + ":" + storename + "/" + + layerName); + } + + return ftDeleted; + + // the store is still there: should we delete it? + + } catch (MalformedURLException ex) { + if (LOGGER.isErrorEnabled()) + LOGGER.error(ex.getLocalizedMessage(), ex); + return false; + } + } + + /** + * Remove recursively a given Datastore in a given Workspace. + * + * @param workspace The name of the workspace + * @param storename The name of the Datastore to remove. + * @return true if the datastore was successfully removed. + * + * @deprecated will be removed in next release use {@link GeoServerRESTPublisher#removeDatastore(String, String, boolean)} + */ + public boolean removeDatastore(String workspace, String storename) { + try { + return removeDatastore(workspace, storename, true); + } catch (IllegalArgumentException e) { + if (LOGGER.isErrorEnabled()) + LOGGER.error("Arguments may not be null or empty!", e); + } + return false; + } + + /** + * Remove a given Datastore in a given Workspace. + * + * @param workspace The name of the workspace + * @param storename The name of the Datastore to remove. + * @param recurse if remove should be performed recursively + * @throws IllegalArgumentException if workspace or storename are null or empty + * @return true if the datastore was successfully removed. + */ + public boolean removeDatastore(String workspace, String storename, final boolean recurse) + throws IllegalArgumentException { + return removeStore(workspace, storename, StoreType.DATASTORES, recurse); + } + + /** + * Remove recursively a given CoverageStore in a given Workspace. + * + * @param workspace The name of the workspace + * @param storename The name of the CoverageStore to remove. + * @return true if the CoverageStore was successfully removed. + * @deprecated use {@link #removeCoverageStore(String, String, boolean)} + */ + public boolean removeCoverageStore(String workspace, String storename) { + return removeCoverageStore(workspace, storename, true); + } + + /** + * Remove a given CoverageStore in a given Workspace. + * + * @param workspace The name of the workspace + * @param storename The name of the CoverageStore to remove. + * @param recurse if remove should be performed recursively + * @return true if the CoverageStore was successfully removed. + */ + public boolean removeCoverageStore(final String workspace, final String storename, + final boolean recurse) throws IllegalArgumentException { + return removeStore(workspace, storename, StoreType.COVERAGESTORES, recurse); + } + + /** + * Remove a given Datastore in a given Workspace. + * + * @param workspace The name of the workspace + * @param storename The name of the Datastore to remove. + * @param the {@link StoreType} type + * @param recurse if remove should be performed recursively + * @throws IllegalArgumentException if workspace or storename are null or empty + * @return true if the store was successfully removed. + */ + private boolean removeStore(String workspace, String storename, StoreType type, + final boolean recurse) throws IllegalArgumentException { + try { + if (workspace == null || storename == null) + throw new IllegalArgumentException("Arguments may not be null!"); + if (workspace.isEmpty() || storename.isEmpty()) + throw new IllegalArgumentException("Arguments may not be empty!"); + + final StringBuilder url = new StringBuilder(restURL); + url.append("/rest/workspaces/").append(workspace).append("/").append(type).append("/") + .append(storename); + if (recurse) + url.append("?recurse=true"); + final URL deleteStore = new URL(url.toString()); + + boolean deleted = HTTPUtils.delete(deleteStore.toExternalForm(), gsuser, gspass); + if (!deleted) { + LOGGER.warn("Could not delete store " + workspace + ":" + storename); + } else { + LOGGER.info("Store successfully deleted " + workspace + ":" + storename); + } + + return deleted; + } catch (MalformedURLException ex) { + if (LOGGER.isErrorEnabled()) + LOGGER.error(ex.getLocalizedMessage(), ex); + return false; + } + } + + /** + * Remove the workspace given Workspace using default parameters + * + * @see {@link GeoServerRESTPublisher#removeWorkspace(String, boolean)} + * @param workspace the workspace to remove + * @return true if success, false otherwise + * @deprecated {@link #removeWorkspace(String, boolean)} + */ + public boolean removeWorkspace(String workspace) { + return removeWorkspace(workspace, false); + } + + /** + * Remove a given Workspace. + * + * @param workspace The name of the workspace + * @param recurse The recurse parameter is used to recursively delete all resources contained by the specified workspace. This includes data + * stores, coverage stores, feature types, etc... Allowable values for this parameter are true or false. The default value is + * false. + * @return true if the WorkSpace was successfully removed. + */ + public boolean removeWorkspace(String workspace, boolean recurse) + throws IllegalArgumentException { + workspace = sanitize(workspace); + try { + if (workspace == null) + throw new IllegalArgumentException("Arguments may not be null!"); + if (workspace.isEmpty()) + throw new IllegalArgumentException("Arguments may not be empty!"); + + StringBuffer url = new StringBuffer(restURL).append("/rest/workspaces/").append( + workspace); + if (recurse) + url.append("?recurse=true"); + + deleteStylesForWorkspace(workspace); // !!! workaround + + final URL deleteUrl = new URL(url.toString()); + boolean deleted = HTTPUtils.delete(deleteUrl.toExternalForm(), gsuser, gspass); + if (!deleted) { + LOGGER.warn("Could not delete Workspace " + workspace); + } else { + LOGGER.info("Workspace successfully deleted " + workspace); + } + + return deleted; + } catch (MalformedURLException ex) { + if (LOGGER.isErrorEnabled()) + LOGGER.error(ex.getLocalizedMessage(), ex); + return false; + } + } + + /** + * workaround: geoserver does not delete styles inside workspaces + * https://jira.codehaus.org/browse/GEOS-5986 + */ + private void deleteStylesForWorkspace(String workspace) { + RESTStyleList styles = styleManager.getStyles(workspace); + if (styles==null) + return; + for (NameLinkElem nameLinkElem : styles) { + removeStyleInWorkspace(workspace, nameLinkElem.getName(), true); + } + } + + /** + * Remove a layer group. + * + * @param workspace the layer group workspace. + * @param name the layer group name. + * @return true if succeeded. + */ + public boolean removeLayerGroup(String workspace, String name) { + String url = restURL + "/rest"; + if (workspace == null) { + url += "/layergroups/" + name; + } else { + url += "/workspaces/" + workspace + "/layergroups/" + name; + } + + try { + URL deleteUrl = new URL(url); + boolean deleted = HTTPUtils.delete(deleteUrl.toExternalForm(), gsuser, gspass); + if (!deleted) { + if (LOGGER.isWarnEnabled()) + LOGGER.warn("Could not delete layergroup " + name); + } else { + if (LOGGER.isInfoEnabled()) + LOGGER.info("Layergroup successfully deleted: " + name); + } + + return deleted; + } catch (MalformedURLException ex) { + if (LOGGER.isErrorEnabled()) + LOGGER.error(ex.getLocalizedMessage(), ex); + return false; + } + } + + /** + * Remove a layer group. + * + * @param name the layer group name. + * @return true if succeeded. + */ + public boolean removeLayerGroup(String name) { + return removeLayerGroup(null, name); + } + + /** + * remove a generic given layer from a given workspace + * + * @param workspace + * @param layerName + * @return true if success + */ + public boolean removeLayer(final String workspace, final String layerName) { + + final String fqLayerName; + + // this null check is here only for backward compatibility. workspace + // shall be mandatory. + if (workspace == null) { + + fqLayerName = layerName; + + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Null workspace while removing layer : " + layerName + + " -- This behavior is deprecated."); + } + } else { + fqLayerName = workspace + ":" + layerName; + } + if (layerName == null) { + if (LOGGER.isErrorEnabled()) { + LOGGER.error("Null layerName : " + layerName); + } + return false; + } + + final String url = restURL + "/rest/layers/" + fqLayerName; + + boolean result = HTTPUtils.delete(url, gsuser, gspass); + if (result) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Layer successfully removed: " + fqLayerName); + } + } else { + if (LOGGER.isWarnEnabled()) + LOGGER.warn("Error removing layer " + fqLayerName); + } + + return result; + } + + // ========================================================================== + // === CATALOG REFRESHING + // ========================================================================== + + /** + * + * /workspaces//datastores/.xml /workspaces//coveragestores/.xml + * + * @param workspace + * @param storeName + * @param mosaicDir + * @return true if successfully reloaded + * @throws FileNotFoundException + * @throws IllegalArgumentException + * @throws MalformedURLException + */ + public boolean reloadStore(String workspace, final String storeName, StoreType storeType) + throws IllegalArgumentException, MalformedURLException { + final String url = HTTPUtils.append(this.restURL, "/rest/workspaces/", workspace, "/", + storeType.toString(), "/", storeName, ".xml").toString(); + final String store = HTTPUtils.get(url, this.gsuser, this.gspass); + + if (store != null) { + String storeTag = storeType.getTypeName(); + // switch (storeType) { + // case COVERAGESTORES: + // storeTag = storeType.toString().replaceAll("store", ""); + // break; + // case DATASTORES: + // storeTag = "featureTypes"; + // break; + // default: + // throw new IllegalArgumentException("Unrecognized type"); + // } + + String startTag = "<" + storeTag + ">"; + int start = store.indexOf(startTag); + String endTag = ""; + int stop = store.indexOf(endTag) + endTag.length(); + return HTTPUtils.putXml(url, store.subSequence(0, start) + store.substring(stop), + this.gsuser, this.gspass) != null ? true : false; + } else + return false; + } + + /** + * Reload the target geoserver configuration + * + * @return true if success + * + * @see GeoServer REST Config API + */ + public boolean reload() { + String sUrl = restURL + "/rest/reload"; + String result = HTTPUtils.post(sUrl, "", "text/plain", gsuser, gspass); + return result != null; + } + + /** + * Reset the target geoserver configuration + * + * @return true if success + * + * @see GeoServer REST Config API + */ + public boolean reset() { + String sUrl = restURL + "/rest/reset"; + String result = HTTPUtils.post(sUrl, "", "text/plain", gsuser, gspass); + return result != null; + } + + // ========================================================================== + // === MISCELLANEOUS + // ========================================================================== + + /** + * Allows to configure some layer attributes such as DefaultStyle + * + * @param workspace + * @param resourceName the name of the resource to use (featureStore or coverageStore name) + * @param layer the layer encoder used to configure the layer + * @return true if success + * @throws IllegalArgumentException if some arguments are null or empty + * + * @TODO WmsPath + */ + public boolean configureLayer(final String workspace, final String resourceName, + final GSLayerEncoder layer) throws IllegalArgumentException { + + if (workspace == null || resourceName == null || layer == null) { + throw new IllegalArgumentException("Null argument"); + } + // TODO: check this usecase, layer should always be defined + if (workspace.isEmpty() || resourceName.isEmpty() || layer.isEmpty()) { + throw new IllegalArgumentException("Empty argument"); + } + + final String fqLayerName = workspace + ":" + resourceName; + + final String url = restURL + "/rest/layers/" + fqLayerName; + + String layerXml = layer.toString(); + String sendResult = HTTPUtils.putXml(url, layerXml, gsuser, gspass); + if (sendResult != null) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Layer successfully configured: " + fqLayerName); + } + } else { + if (LOGGER.isWarnEnabled()) + LOGGER.warn("Error configuring layer " + fqLayerName + " (" + sendResult + ")"); + } + + return sendResult != null; + } + + /** + * Create a new LayerGroup using the specified encoder + * + * @param name name of the layer group + * @param group group encoder + * @return true if operation was successful + */ + public boolean createLayerGroup(String name, GSLayerGroupEncoder group) { + return createLayerGroup(null, name, group); + } + + /** + * Create a new LayerGroup using the specified encoder + * + * @param workspace name of the workspace + * @param name name of the layer group + * @param group group encoder + * @return true if operation was successful + */ + public boolean createLayerGroup(String workspace, String name, GSLayerGroupEncoder group) { + String url = restURL + "/rest"; + if (workspace == null) { + url += "/layergroups/"; + } else { + group.setWorkspace(workspace); + url += "/workspaces/" + workspace + "/layergroups/"; + } + + group.setName(name); + + String sendResult = HTTPUtils.postXml(url, group.toString(), gsuser, gspass); + if (sendResult != null) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("LayerGroup successfully configured: " + name); + } + } else { + if (LOGGER.isWarnEnabled()) + LOGGER.warn("Error configuring LayerGroup " + name + " (" + sendResult + ")"); + } + + return sendResult != null; + } + + /** + * Update a LayerGroup using the specified encoder + * + * @param name name of the layer group + * @param group group encoder + * @return true if operation was successful + */ + public boolean configureLayerGroup(String name, GSLayerGroupEncoder group) { + return configureLayerGroup(null, name, group); + } + + /** + * Update a LayerGroup using the specified encoder + * + * @param workspace name of the workspace + * @param name name of the layer group + * @param group group encoder + * @return true if operation was successful + */ + public boolean configureLayerGroup(String workspace, String name, GSLayerGroupEncoder group) { + String url = restURL + "/rest"; + if (workspace == null) { + url += "/layergroups/" + name; + } else { + url += "/workspaces/" + workspace + "/layergroups/" + name; + } + + String sendResult = HTTPUtils.putXml(url, group.toString(), gsuser, gspass); + if (sendResult != null) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("LayerGroup successfully configured: " + name); + } + } else { + if (LOGGER.isWarnEnabled()) + LOGGER.warn("Error configuring LayerGroup " + name + " (" + sendResult + ")"); + } + + return sendResult != null; + } + + /** + * Configure an existing coverage in a given workspace and coverage store + * + * @param ce contains the coverage name to configure and the configuration to apply + * @param wsname the workspace to search for existent coverage + * @param csname the coverage store to search for existent coverage + * @return true if success + */ + public boolean configureCoverage(final GSCoverageEncoder ce, final String wsname, + final String csname) { + return configureCoverage(ce, wsname, csname, ce.getName()); + } + + /** + * Configure an existing coverage in a given workspace and coverage store + * + * @param ce contains the coverage name to configure and the configuration to apply + * @param wsname the workspace to search for existent coverage + * @param csname the coverage store to search for existent coverage + * @param coverageName the name of the coverage, useful for changing name for the coverage itself + * @return true if success + */ + public boolean configureCoverage(final GSCoverageEncoder ce, final String wsname, + final String csname, final String coverageName) { + if (coverageName == null) { + if (LOGGER.isErrorEnabled()) + LOGGER.error("Unable to configure a coverage with no name try using GSCoverageEncoder.setName(String)"); + return false; + } + // retrieve coverage name + GeoServerRESTReader reader; + try { + reader = new GeoServerRESTReader(restURL, gsuser, gspass); + } catch (MalformedURLException e) { + if (LOGGER.isErrorEnabled()) + LOGGER.error(e.getLocalizedMessage(), e); + return false; + } + + // optimized search, left the old code for reference + RESTCoverage coverage = reader.getCoverage(wsname, csname, coverageName); +// final RESTCoverageList covList = reader.getCoverages(wsname, csname); +// if (covList==null||covList.isEmpty()) { +// if (LOGGER.isErrorEnabled()) +// LOGGER.error("No coverages found in new coveragestore " + csname); +// return false; +// } +// final Iterator it = covList.iterator(); +// while (it.hasNext()) { +// NameLinkElem nameElem = it.next(); +// if (nameElem.getName().equals(coverageName)) { +// found = true; +// break; +// } +// } + // if no coverage to configure is found return false + if (coverage==null) { + if (LOGGER.isErrorEnabled()) + LOGGER.error("No coverages found in new coveragestore " + csname + " called " + + coverageName); + return false; + } + + // configure the selected coverage + final String url = restURL + "/rest/workspaces/" + wsname + "/coveragestores/" + csname + + "/coverages/" + coverageName + ".xml"; + + final String xmlBody = ce.toString(); + final String sendResult = HTTPUtils.putXml(url, xmlBody, gsuser, gspass); + if (sendResult != null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Coverage successfully configured " + wsname + ":" + csname + ":" + + coverageName); + } + } else { + if (LOGGER.isWarnEnabled()) + LOGGER.warn("Error configuring coverage " + wsname + ":" + csname + ":" + coverageName + + " (" + sendResult + ")"); + } + + return sendResult != null; + } + + /** + * @deprecated use {@link #createCoverage(String, String, GSCoverageEncoder)} + */ + public boolean createCoverage(final GSCoverageEncoder ce, final String wsname, + final String csname) { + return createCoverage(wsname, csname, ce); + } + + /** + * Create a new coverage in a given workspace and coverage store + * + * @param wsname the workspace to search for existent coverage + * @param storeName an existent store name to use as data source + * @param ce contains the coverage name to create and the configuration to apply + * @return true if success + * @throws IllegalArgumentException if arguments are null or empty + */ + public boolean createCoverage(final String wsname, final String storeName, + final GSCoverageEncoder ce) throws IllegalArgumentException { + return createResource(wsname, StoreType.COVERAGESTORES, storeName, ce); + } + + /** + * Create a new resource in a given workspace and store + * + * @param wsname the workspace to search for existent coverage + * @param storeName an existent store name to use as data source + * @param re contains the coverage name to create and the configuration to apply + * + * @TODO For FeatureType: The list parameter is used to control the category of feature types that are returned. It can take one of the three + * values configured, available, or all. + * + * configured - Only setup or configured feature types are returned. This is the default value. available - Only unconfigured feature types + * (not yet setup) but are available from the specified datastore will be returned. available_with_geom - Same as available but only + * includes feature types that have a geometry granule. all - The union of configured and available. + * + * + * @return true if success + * @throws IllegalArgumentException if arguments are null or empty + */ + private boolean createResource(String workspace, StoreType dsType, String storeName, + GSResourceEncoder re) throws IllegalArgumentException { + if (workspace == null || dsType == null || storeName == null || re == null) { + throw new IllegalArgumentException("Null argument"); + } + StringBuilder sbUrl = new StringBuilder(restURL).append("/rest/workspaces/") + .append(workspace).append("/").append(dsType).append("/").append(storeName) + .append("/").append(dsType.getTypeNameWithFormat(Format.XML)); + + final String resourceName = re.getName(); + if (resourceName == null) { + throw new IllegalArgumentException( + "Unable to configure a coverage using unnamed coverage encoder"); + } + + final String xmlBody = re.toString(); + final String sendResult = HTTPUtils.postXml(sbUrl.toString(), xmlBody, gsuser, gspass); + if (sendResult != null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(dsType + " successfully created " + workspace + ":" + storeName + ":" + + resourceName); + } + } else { + if (LOGGER.isErrorEnabled()) + LOGGER.error("Error creating coverage " + workspace + ":" + storeName + ":" + + resourceName + " (" + sendResult + ")"); + } + + return sendResult != null; + } + + /** + * Appends ".DUMMY" to any string containing a dot (sic). + */ + protected String sanitize(String s) { + if (s.indexOf(".") != -1) { + return s + ".DUMMY"; + } + return s; + } + + /** + * Append params generating a string in the form: + *

+ * NAME_0=VALUE_0&NAME_1=VALUE_1&....&NAME_n-1=VALUE_n-1 + *

+ *
+ * + * @param params an array of NameValuePair + * @return the parameter string or empty an string + */ + private String appendParameters(NameValuePair... params) { + StringBuilder sbUrl = new StringBuilder(); + // append parameters + if (params != null) { + final int paramsSize = params.length; + if (paramsSize > 0) { + int i = 0; + NameValuePair param = params[i]; + while (param != null && i++ < paramsSize) { + final String name = param.getName(); + final String value = param.getValue(); + // success + if (name != null && !name.isEmpty() && value != null && !value.isEmpty()) { + sbUrl.append(name).append("=").append(value); + // end cycle + param = null; + } else { + // next value + param = params[i]; + } + } + for (; i < paramsSize; i++) { + param = params[i]; + if (param != null) { + final String name = param.getName(); + final String value = param.getValue(); + sbUrl.append(name).append("=").append(value); + if (name != null && !name.isEmpty() && value != null && !value.isEmpty()) { + sbUrl.append("&").append(name).append("=").append(value); + } + + } + + } + } + } + return sbUrl.toString(); + } + + /** + * URL-encodes a String. + * + * @param s The original string. + * @return The encoded string. + */ + protected String encode(String s) { + // try { + // return URLEncoder.encode(s,"UTF-8"); + // } catch (UnsupportedEncodingException e) { + // LOGGER.warn("Error encoding :"+s+" with UTF-8: "+e.getLocalizedMessage()); + return URLEncoder.encode(s); + // } + } + + // ==> StructuredCoverageGridReader + + /** + * Create a store or harvest the coverage from the provided external path. + * + * @param workspace the GeoServer workspace + * @param coverageStore the GeoServer coverageStore + * @param format the format of the file to upload + * @param the absolut path to the file to upload + * + * @return true if the call succeeds or false otherwise. + */ + public boolean harvestExternal(String workspace, String coverageStore, String format, + String path) { + try { + GeoServerRESTStructuredGridCoverageReaderManager manager = new GeoServerRESTStructuredGridCoverageReaderManager( + new URL(restURL), gsuser, gspass); + return manager.harvestExternal(workspace, coverageStore, format, path); + } catch (IllegalArgumentException e) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info(e.getLocalizedMessage(), e); + } + } catch (MalformedURLException e) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info(e.getLocalizedMessage(), e); + } + } + return false; + } + + /** + * Create a new ImageMosaic with the provided configuration provided as a zip file. + * + *

+ * This call configures all the coverages contained in the ImageMosaic. + * + * @param workspace the GeoServer workspace + * @param coverageStore the GeoServer coverageStore + * @param the absolute path to the file to upload + * + * @return true if the call succeeds or false otherwise. + * @since geoserver-2.4.0, geoserver-mng-1.6.0 + */ + public boolean createImageMosaic(String workspace, String coverageStore, String path) { + return createImageMosaic(workspace, coverageStore, path, ConfigureCoveragesOption.ALL); + } + + /** + * Create a new ImageMosaic with the provided configuration provided as a zip file. + * + *

+ * With the options configure we can decide whether or not to configure or not the coverages contained in the ImageMosaic. + * + * @param workspace the GeoServer workspace + * @param coverageStore the GeoServer coverageStore + * @param the absolute path to the file to upload + * @param configureOpt tells GeoServer whether to configure all coverages in this mosaic (ALL) or none of them (NONE). + * + * @return true if the call succeeds or false otherwise. + * @since geoserver-2.4.0, geoserver-mng-1.6.0 + */ + public boolean createImageMosaic(String workspace, String coverageStore, String path, ConfigureCoveragesOption configureOpt) { + // checks + checkString(workspace); + checkString(coverageStore); + checkString(path); + final File zipFile= new File(path); + if(!zipFile.exists()||!zipFile.isFile()||!zipFile.canRead()){ + throw new IllegalArgumentException("The provided pathname does not point to a valide zip file: "+path); + } + // is it a zip? + ZipFile zip=null; + try{ + zip= new ZipFile(zipFile); + zip.getName(); + }catch (Exception e) { + LOGGER.trace(e.getLocalizedMessage(),e.getStackTrace()); + throw new IllegalArgumentException("The provided pathname does not point to a valide zip file: "+path); + }finally{ + if(zip!=null){ + try { + zip.close(); + } catch (IOException e) { + // swallow + LOGGER.trace(e.getLocalizedMessage(),e.getStackTrace()); + } + } + } + + // create URL + StringBuilder ss=HTTPUtils.append(restURL, "/rest/workspaces/", workspace, "/coveragestores/", + coverageStore, "/", UploadMethod.EXTERNAL.toString(), ".imagemosaic"); + switch(configureOpt){ + case ALL: + break; + case NONE: + ss.append("?configure=none"); + break; + default: + throw new IllegalArgumentException("Unrecognized COnfigureOption: "+configureOpt); + } + String sUrl = ss.toString(); + + // POST request + String result = HTTPUtils.put(sUrl, zipFile, "application/zip", gsuser, gspass); + return result != null; + } + + /** + * Remove a granule from a structured coverage by id. + * + * @param workspace the GeoServer workspace + * @param coverageStore the GeoServer coverageStore + * @param coverage the name of the target coverage from which we are going to remove + * @param filter the absolute path to the file to upload + * + * @return null in case the call does not succeed, or an instance of {@link RESTStructuredCoverageGranulesList}. + * + * @throws MalformedURLException + * @throws UnsupportedEncodingException + */ + public boolean removeGranuleById(final String workspace, String coverageStore, String coverage, + String granuleId) { + try { + GeoServerRESTStructuredGridCoverageReaderManager manager = new GeoServerRESTStructuredGridCoverageReaderManager( + new URL(restURL), gsuser, gspass); + return manager.removeGranuleById(workspace, coverageStore, coverage, granuleId); + } catch (IllegalArgumentException e) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info(e.getLocalizedMessage(), e); + } + } catch (MalformedURLException e) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info(e.getLocalizedMessage(), e); + } + } + return false; + } + + /** + * Remove granules from a structured coverage, by providing a CQL filter. + * + * @param workspace the GeoServer workspace + * @param coverageStore the GeoServer coverageStore + * @param coverage the name of the target coverage from which we are going to remove + * @param filter the absolute path to the file to upload + * + * @return null in case the call does not succeed, or an instance of {@link RESTStructuredCoverageGranulesList}. + * + * @throws MalformedURLException + * @throws UnsupportedEncodingException + */ + public boolean removeGranulesByCQL(final String workspace, String coverageStore, + String coverage, String filter) throws UnsupportedEncodingException { + try { + GeoServerRESTStructuredGridCoverageReaderManager manager = new GeoServerRESTStructuredGridCoverageReaderManager( + new URL(restURL), gsuser, gspass); + return manager.removeGranulesByCQL(workspace, coverageStore, coverage, filter); + } catch (IllegalArgumentException e) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info(e.getLocalizedMessage(), e); + } + } catch (MalformedURLException e) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info(e.getLocalizedMessage(), e); + } + } + return false; + + } + + /** + * Check the provided string for not being null or empty. + * + *

+ * It throws an exception in case the string is either null or empty. + * + * @param string the {@link String} to be checked + */ + private static void checkString(String string) { + if (string == null) { + throw new NullPointerException("Provided string is is null!"); + } + if (string.length() <= 0) { + throw new IllegalArgumentException("Provided string is is empty!"); + } + + } + +} diff --git a/src/main/java/it/geosolutions/geoserver/rest/manager/GeoServerRESTStyleManager.java b/src/main/java/it/geosolutions/geoserver/rest/manager/GeoServerRESTStyleManager.java index b66f899..50fa601 100644 --- a/src/main/java/it/geosolutions/geoserver/rest/manager/GeoServerRESTStyleManager.java +++ b/src/main/java/it/geosolutions/geoserver/rest/manager/GeoServerRESTStyleManager.java @@ -260,6 +260,15 @@ public class GeoServerRESTStyleManager extends GeoServerRESTAbstractManager { return result != null; } + /** + * Store and publish a Style, assigning it a name and choosing the raw format. + * + * @param sldFile the File containing the SLD document. + * @param name the Style name. + * @param raw the raw format + * + * @return true if the operation completed successfully. + */ public boolean publishStyle(final File sldFile, final String name, final boolean raw) { /* * This is the equivalent call with cUrl: