package mil.nga.giat.geowave.cli.geoserver; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.ws.rs.PathParam; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.glassfish.jersey.SslConfigurator; import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.beust.jcommander.ParameterException; import static mil.nga.giat.geowave.cli.geoserver.constants.GeoServerConstants.*; import mil.nga.giat.geowave.adapter.raster.adapter.RasterDataAdapter; import mil.nga.giat.geowave.adapter.vector.GeotoolsFeatureDataAdapter; import mil.nga.giat.geowave.cli.geoserver.GeoServerAddLayerCommand.AddOption; import mil.nga.giat.geowave.core.cli.operations.config.security.crypto.BaseEncryption; import mil.nga.giat.geowave.core.cli.operations.config.security.utils.SecurityUtils; import mil.nga.giat.geowave.core.cli.utils.FileUtils; import mil.nga.giat.geowave.core.store.CloseableIterator; import mil.nga.giat.geowave.core.store.adapter.AdapterStore; import mil.nga.giat.geowave.core.store.adapter.DataAdapter; import mil.nga.giat.geowave.core.store.operations.remote.options.DataStorePluginOptions; import mil.nga.giat.geowave.core.store.operations.remote.options.StoreLoader; import net.sf.json.JSONArray; import net.sf.json.JSONObject; public class GeoServerRestClient { private final static Logger LOGGER = LoggerFactory.getLogger(GeoServerRestClient.class); private final static int defaultIndentation = 2; static private class DataAdapterInfo { String adapterId; Boolean isRaster; } private final GeoServerConfig config; private WebTarget webTarget = null; public GeoServerRestClient( final GeoServerConfig config ) { this.config = config; org.apache.log4j.Logger.getRootLogger().setLevel( org.apache.log4j.Level.DEBUG); } public GeoServerRestClient( final GeoServerConfig config, WebTarget webTarget ) { this.config = config; this.webTarget = webTarget; org.apache.log4j.Logger.getRootLogger().setLevel( org.apache.log4j.Level.DEBUG); } /** * * @return */ public GeoServerConfig getConfig() { return config; } private WebTarget getWebTarget() { if (webTarget == null) { String url = getConfig().getUrl(); if (url != null) { url = url.trim(); Client client = null; if (url.toLowerCase().startsWith( "http://")) { client = ClientBuilder.newClient(); } else if (url.toLowerCase().startsWith( "https://")) { SslConfigurator sslConfig = SslConfigurator.newInstance(); if (getConfig().getGsConfigProperties() != null) { loadSSLConfigurations( sslConfig, getConfig().getGsConfigProperties()); } SSLContext sslContext = sslConfig.createSSLContext(); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); client = ClientBuilder.newBuilder().sslContext( sslContext).build(); } if (client != null) { client.register(HttpAuthenticationFeature.basic( getConfig().getUser(), getConfig().getPass())); webTarget = client.target(url); } } } return webTarget; } /** * If connecting to GeoServer over HTTPS (HTTP+SSL), we need to specify the * SSL properties. The SSL properties are set from a properties file. Since * the properties will be different, based on one GeoServer deployment * compared to another, this gives the ability to specify any of the fields. * If the key is in provided properties file, it will be loaded into the * GeoServer SSL configuration. * * @param sslConfig * SSL Configuration object for use when instantiating an HTTPS * connection to GeoServer * @param gsConfigProperties * Properties object with applicable GeoServer connection * properties */ private void loadSSLConfigurations( SslConfigurator sslConfig, Properties gsConfigProperties ) { if (gsConfigProperties != null && sslConfig != null) { // default to TLS for geoserver ssl security protocol sslConfig.securityProtocol(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_SECURITY_PROTOCOL, "TLS")); // check truststore property settings if (gsConfigProperties.containsKey(GEOSERVER_SSL_TRUSTSTORE_FILE)) { // resolve file path - either relative or absolute - then get // the canonical path File trustStoreFile = new File( getPropertyValue( gsConfigProperties, GEOSERVER_SSL_TRUSTSTORE_FILE)); if (trustStoreFile != null) { try { sslConfig.trustStoreFile(trustStoreFile.getCanonicalPath()); } catch (IOException e) { LOGGER.error( "An error occurred loading the truststore at the specified path [" + getPropertyValue( gsConfigProperties, GEOSERVER_SSL_TRUSTSTORE_FILE) + "]:" + e.getLocalizedMessage(), e); } } } if (gsConfigProperties.containsKey(GEOSERVER_SSL_TRUSTSTORE_PASS)) { sslConfig.trustStorePassword(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_TRUSTSTORE_PASS)); } if (gsConfigProperties.containsKey(GEOSERVER_SSL_TRUSTSTORE_TYPE)) { sslConfig.trustStoreType(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_TRUSTSTORE_TYPE)); } if (gsConfigProperties.containsKey(GEOSERVER_SSL_TRUSTSTORE_PROVIDER)) { sslConfig.trustStoreProvider(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_TRUSTSTORE_PROVIDER)); } if (gsConfigProperties.containsKey(GEOSERVER_SSL_TRUSTMGR_ALG)) { sslConfig.trustManagerFactoryAlgorithm(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_TRUSTMGR_ALG)); } if (gsConfigProperties.containsKey(GEOSERVER_SSL_TRUSTMGR_PROVIDER)) { sslConfig.trustManagerFactoryProvider(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_TRUSTMGR_PROVIDER)); } // check keystore property settings if (gsConfigProperties.containsKey(GEOSERVER_SSL_KEYSTORE_FILE)) { // resolve file path - either relative or absolute - then get // the canonical path File keyStoreFile = new File( FileUtils.formatFilePath(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_KEYSTORE_FILE))); if (keyStoreFile != null) { try { sslConfig.keyStoreFile(keyStoreFile.getCanonicalPath()); } catch (IOException e) { LOGGER.error( "An error occurred loading the keystore at the specified path [" + getPropertyValue( gsConfigProperties, GEOSERVER_SSL_KEYSTORE_FILE) + "]:" + e.getLocalizedMessage(), e); } } } if (gsConfigProperties.containsKey(GEOSERVER_SSL_KEYSTORE_PASS)) { sslConfig.keyStorePassword(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_KEYSTORE_PASS)); } if (gsConfigProperties.containsKey(GEOSERVER_SSL_KEY_PASS)) { sslConfig.keyPassword(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_KEY_PASS)); } if (gsConfigProperties.containsKey(GEOSERVER_SSL_KEYSTORE_PROVIDER)) { sslConfig.keyStoreProvider(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_KEYSTORE_PROVIDER)); } if (gsConfigProperties.containsKey(GEOSERVER_SSL_KEYSTORE_TYPE)) { sslConfig.keyStoreType(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_KEYSTORE_TYPE)); } if (gsConfigProperties.containsKey(GEOSERVER_SSL_KEYMGR_ALG)) { sslConfig.keyManagerFactoryAlgorithm(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_KEYMGR_ALG)); } if (gsConfigProperties.containsKey(GEOSERVER_SSL_KEYMGR_PROVIDER)) { sslConfig.keyManagerFactoryProvider(getPropertyValue( gsConfigProperties, GEOSERVER_SSL_KEYMGR_PROVIDER)); } } } private String getPropertyValue( final Properties configProps, final String configKey ) { return getPropertyValue( configProps, configKey, null); } private String getPropertyValue( final Properties configProps, final String configKey, final String defaultValue ) { String configValue = defaultValue; if (configProps != null) { configValue = configProps.getProperty( configKey, defaultValue); if (BaseEncryption.isProperlyWrapped(configValue)) { try { final File resourceTokenFile = SecurityUtils.getFormattedTokenKeyFileForConfig(getConfig() .getPropFile()); // if password in config props is encrypted, need to decrypt // it configValue = SecurityUtils.decryptHexEncodedValue( configValue, resourceTokenFile.getCanonicalPath()); return configValue; } catch (Exception e) { LOGGER.error( "An error occurred decrypting password: " + e.getLocalizedMessage(), e); return configValue; } } } return configValue; } /** * Convenience - add layer(s) for the given store to geoserver * * @param workspaceName * @param storeName * @param adapterId * @param defaultStyle * @return */ public Response addLayer( final String workspaceName, final String storeName, final String adapterId, final String defaultStyle ) { // retrieve the adapter info list for the store final ArrayList<DataAdapterInfo> adapterInfoList = getStoreAdapterInfo( storeName, adapterId); LOGGER.debug("Finished retrieving adapter list"); if ((adapterInfoList.size() > 1) && (adapterId == null)) { LOGGER.debug("addlayer doesn't know how to deal with multiple adapters"); final String descr = "Please use -a, or choose one of these with -id:"; final JSONObject jsonObj = getJsonFromAdapters( adapterInfoList, descr); LOGGER.debug(jsonObj.toString()); return Response.ok( jsonObj.toString(defaultIndentation)).build(); } // verify the workspace exists if (!workspaceExists(workspaceName)) { LOGGER.debug("addlayer needs to create the " + workspaceName + " workspace"); final Response addWsResponse = addWorkspace(workspaceName); if (addWsResponse.getStatus() != Status.CREATED.getStatusCode()) { return addWsResponse; } } final String cvgStoreName = storeName + GeoServerConfig.DEFAULT_CS; final String dataStoreName = storeName + GeoServerConfig.DEFAULT_DS; // iterate through data adapters for (final DataAdapterInfo dataAdapterInfo : adapterInfoList) { // handle coverage stores & coverages if (dataAdapterInfo.isRaster) { // verify coverage store exists final Response getCsResponse = getCoverageStore( workspaceName, cvgStoreName); if (getCsResponse.getStatus() == Status.NOT_FOUND.getStatusCode()) { final Response addCsResponse = addCoverageStore( workspaceName, cvgStoreName, storeName, null, null, null); if (addCsResponse.getStatus() != Status.CREATED.getStatusCode()) { return addCsResponse; } } else if (getCsResponse.getStatus() != Status.OK.getStatusCode()) { return getCsResponse; } // See if the coverage already exists final Response getCvResponse = getCoverage( workspaceName, cvgStoreName, dataAdapterInfo.adapterId); if (getCvResponse.getStatus() == Status.OK.getStatusCode()) { LOGGER.debug(dataAdapterInfo.adapterId + " layer already exists"); continue; } // We have a coverage store. Add the layer per the adapter ID final Response addCvResponse = addCoverage( workspaceName, cvgStoreName, dataAdapterInfo.adapterId); if (addCvResponse.getStatus() != Status.CREATED.getStatusCode()) { return addCvResponse; } } // handle datastores and feature layers else { // verify datastore exists final Response getDsResponse = getDatastore( workspaceName, dataStoreName); if (getDsResponse.getStatus() == Status.NOT_FOUND.getStatusCode()) { final Response addDsResponse = addDatastore( workspaceName, dataStoreName, storeName); if (addDsResponse.getStatus() != Status.CREATED.getStatusCode()) { return addDsResponse; } } else if (getDsResponse.getStatus() != Status.OK.getStatusCode()) { return getDsResponse; } LOGGER.debug("Checking for existing feature layer: " + dataAdapterInfo.adapterId); // See if the feature layer already exists final Response getFlResponse = getFeatureLayer(dataAdapterInfo.adapterId); if (getFlResponse.getStatus() == Status.OK.getStatusCode()) { LOGGER.debug(dataAdapterInfo.adapterId + " layer already exists"); continue; } LOGGER.debug("Get feature layer: " + dataAdapterInfo.adapterId + " returned " + getFlResponse.getStatus()); // We have a datastore. Add the layer per the adapter ID final Response addFlResponse = addFeatureLayer( workspaceName, dataStoreName, dataAdapterInfo.adapterId, defaultStyle); if (addFlResponse.getStatus() != Status.CREATED.getStatusCode()) { return addFlResponse; } } } // Report back to the caller the adapter IDs and the types that were // used to create the layers final JSONObject jsonObj = getJsonFromAdapters( adapterInfoList, "Successfully added:"); return Response.ok( jsonObj.toString(defaultIndentation)).build(); } /** * Get JSON object(s) from adapter list * * @param adapterInfoList * @param description * @return JSONObject */ private JSONObject getJsonFromAdapters( final ArrayList<DataAdapterInfo> adapterInfoList, final String description ) { final StringBuffer buf = new StringBuffer(); // If we made it this far, let's just iterate through the adapter IDs // and build the JSON response data buf.append("{'description':'" + description + "', " + "'layers':["); for (int i = 0; i < adapterInfoList.size(); i++) { final DataAdapterInfo info = adapterInfoList.get(i); buf.append("{'id':'" + info.adapterId + "',"); buf.append("'type':'" + (info.isRaster ? "raster" : "vector") + "'}"); if (i < (adapterInfoList.size() - 1)) { buf.append(","); } } buf.append("]}"); return JSONObject.fromObject(buf.toString()); } /** * Check if workspace exists * * @param workspace * @return true if workspace exists, false if not */ public boolean workspaceExists( String workspace ) { if (workspace == null) { workspace = config.getWorkspace(); } final Response getWsResponse = getWorkspaces(); if (getWsResponse.getStatus() == Status.OK.getStatusCode()) { final JSONObject jsonResponse = JSONObject.fromObject(getWsResponse.getEntity()); final JSONArray workspaces = jsonResponse.getJSONArray("workspaces"); for (int i = 0; i < workspaces.size(); i++) { final String wsName = workspaces.getJSONObject( i).getString( "name"); if (wsName.equals(workspace)) { return true; } } } else { LOGGER.error("Error retrieving GeoServer workspace list"); } return false; } /** * Get list of workspaces from geoserver * * @return */ public Response getWorkspaces() { final Response resp = getWebTarget().path( "rest/workspaces.json").request().get(); if (resp.getStatus() == Status.OK.getStatusCode()) { resp.bufferEntity(); // get the workspace names final JSONArray workspaceArray = getArrayEntryNames( JSONObject.fromObject(resp.readEntity(String.class)), "workspaces", "workspace"); final JSONObject workspacesObj = new JSONObject(); workspacesObj.put( "workspaces", workspaceArray); return Response.ok( workspacesObj.toString(defaultIndentation)).build(); } return resp; } /** * Add workspace to geoserver * * @param workspace * @return */ public Response addWorkspace( final String workspace ) { return getWebTarget().path( "rest/workspaces").request().post( Entity.entity( "{'workspace':{'name':'" + workspace + "'}}", MediaType.APPLICATION_JSON)); } /** * Delete workspace from geoserver * * @param workspace * @return */ public Response deleteWorkspace( final String workspace ) { return getWebTarget().path( "rest/workspaces/" + workspace).queryParam( "recurse", "true").request().delete(); } /** * Get the string version of a datastore JSONObject from geoserver * * @param workspaceName * @param datastoreName * @return */ public Response getDatastore( final String workspaceName, final String datastoreName ) { final Response resp = getWebTarget().path( "rest/workspaces/" + workspaceName + "/datastores/" + datastoreName + ".json").request().get(); if (resp.getStatus() == Status.OK.getStatusCode()) { resp.bufferEntity(); final JSONObject datastore = JSONObject.fromObject(resp.readEntity(String.class)); if (datastore != null) { return Response.ok( datastore.toString(defaultIndentation)).build(); } } return resp; } /** * Get list of Datastore names from geoserver * * @param workspaceName * @return */ public Response getDatastores( final String workspaceName ) { final Response resp = getWebTarget().path( "rest/workspaces/" + workspaceName + "/datastores.json").request().get(); if (resp.getStatus() == Status.OK.getStatusCode()) { resp.bufferEntity(); // get the datastore names final JSONArray datastoreArray = getArrayEntryNames( JSONObject.fromObject(resp.readEntity(String.class)), "dataStores", "dataStore"); final JSONObject dsObj = new JSONObject(); dsObj.put( "dataStores", datastoreArray); return Response.ok( dsObj.toString(defaultIndentation)).build(); } return resp; } /** * Add a geowave datastore to geoserver * * @param workspaceName * @param datastoreName * @param gwStoreName * @return */ public Response addDatastore( final String workspaceName, String datastoreName, final String gwStoreName ) { final DataStorePluginOptions inputStoreOptions = getStorePlugin(gwStoreName); if ((datastoreName == null) || datastoreName.isEmpty()) { datastoreName = gwStoreName + GeoServerConfig.DEFAULT_DS; } final String lockMgmt = "memory"; final String authMgmtPrvdr = "empty"; final String authDataUrl = ""; final String queryIndexStrategy = "Best Match"; final String dataStoreJson = createDatastoreJson( inputStoreOptions.getType(), inputStoreOptions.getOptionsAsMap(), datastoreName, lockMgmt, authMgmtPrvdr, authDataUrl, queryIndexStrategy, true); // create a new geoserver style return getWebTarget().path( "rest/workspaces/" + workspaceName + "/datastores").request().post( Entity.entity( dataStoreJson, MediaType.APPLICATION_JSON)); } /** * Delete a geowave datastore from geoserver * * @param workspaceName * @param datastoreName * @return */ public Response deleteDatastore( final String workspaceName, final String datastoreName ) { return getWebTarget().path( "rest/workspaces/" + workspaceName + "/datastores/" + datastoreName).queryParam( "recurse", "true").request().delete(); } /** * Get a layer from geoserver * * @param layerName * @return */ public Response getFeatureLayer( final String layerName ) { final Response resp = getWebTarget().path( "rest/layers/" + layerName + ".json").request().get(); if (resp.getStatus() == Status.OK.getStatusCode()) { final JSONObject layer = JSONObject.fromObject(resp.readEntity(String.class)); if (layer != null) { return Response.ok( layer.toString(defaultIndentation)).build(); } } return resp; } /** * Get list of layers from geoserver * * @param workspaceName * : if null, don't filter on workspace * @param datastoreName * : if null, don't filter on datastore * @param geowaveOnly * : if true, only return geowave layers * @return */ public Response getFeatureLayers( final String workspaceName, final String datastoreName, final boolean geowaveOnly ) { final boolean wsFilter = ((workspaceName != null) && !workspaceName.isEmpty()); final boolean dsFilter = ((datastoreName != null) && !datastoreName.isEmpty()); final Response resp = getWebTarget().path( "rest/layers.json").request().get(); if (resp.getStatus() == Status.OK.getStatusCode()) { resp.bufferEntity(); // get the datastore names final JSONArray layerArray = getArrayEntryNames( JSONObject.fromObject(resp.readEntity(String.class)), "layers", "layer"); // holder for simple layer info (when geowaveOnly = false) final JSONArray layerInfoArray = new JSONArray(); final Map<String, List<String>> namespaceLayersMap = new HashMap<String, List<String>>(); final Pattern p = Pattern.compile("workspaces/(.*?)/datastores/(.*?)/"); for (int i = 0; i < layerArray.size(); i++) { final boolean include = !geowaveOnly && !wsFilter && !dsFilter; // no // filtering // of // any // kind if (include) { // just grab it... layerInfoArray.add(layerArray.getJSONObject(i)); continue; // and move on } // at this point, we are filtering somehow. get some more info // about the layer final String name = layerArray.getJSONObject( i).getString( "name"); final String layer = (String) getFeatureLayer( name).getEntity(); // get the workspace and name for each datastore String ws = null; String ds = null; final Matcher m = p.matcher(layer); if (m.find()) { ws = m.group(1); ds = m.group(2); } // filter on datastore? if (!dsFilter || ((ds != null) && ds.equals(datastoreName))) { // filter on workspace? if (!wsFilter || ((ws != null) && ws.equals(workspaceName))) { final JSONObject datastore = JSONObject.fromObject( getDatastore( ds, ws).getEntity()).getJSONObject( "dataStore"); // only process GeoWave layers if (geowaveOnly) { if ((datastore != null) && datastore.containsKey("type") && datastore.getString( "type").startsWith( "GeoWave Datastore")) { JSONArray entryArray = null; if (datastore.get("connectionParameters") instanceof JSONObject) { entryArray = datastore.getJSONObject( "connectionParameters").getJSONArray( "entry"); } else if (datastore.get("connectionParameters") instanceof JSONArray) { entryArray = datastore.getJSONArray( "connectionParameters").getJSONObject( 0).getJSONArray( "entry"); } if (entryArray == null) { LOGGER .error("entry Array is null - didn't find a connectionParameters datastore object that was a JSONObject or JSONArray"); } else { // group layers by namespace for (int j = 0; j < entryArray.size(); j++) { final JSONObject entry = entryArray.getJSONObject(j); final String key = entry.getString("@key"); final String value = entry.getString("$"); if (key.startsWith("gwNamespace")) { if (namespaceLayersMap.containsKey(value)) { namespaceLayersMap.get( value).add( name); } else { final ArrayList<String> layers = new ArrayList<String>(); layers.add(name); namespaceLayersMap.put( value, layers); } break; } } } } } else { // just get all the layers from this store layerInfoArray.add(layerArray.getJSONObject(i)); } } } } // Handle geowaveOnly response if (geowaveOnly) { // create the json object with layers sorted by namespace final JSONArray layersArray = new JSONArray(); for (final Map.Entry<String, List<String>> kvp : namespaceLayersMap.entrySet()) { final JSONArray layers = new JSONArray(); for (int i = 0; i < kvp.getValue().size(); i++) { final JSONObject layerObj = new JSONObject(); layerObj.put( "name", kvp.getValue().get( i)); layers.add(layerObj); } final JSONObject layersObj = new JSONObject(); layersObj.put( "namespace", kvp.getKey()); layersObj.put( "layers", layers); layersArray.add(layersObj); } final JSONObject layersObj = new JSONObject(); layersObj.put( "layers", layersArray); return Response.ok( layersObj.toString(defaultIndentation)).build(); } else { final JSONObject layersObj = new JSONObject(); layersObj.put( "layers", layerInfoArray); return Response.ok( layersObj.toString(defaultIndentation)).build(); } } return resp; } /** * Add feature layer to geoserver * * @param workspaceName * @param datastoreName * @param layerName * @param defaultStyle * @return */ public Response addFeatureLayer( final String workspaceName, final String datastoreName, final String layerName, final String defaultStyle ) { if (defaultStyle != null) { getWebTarget().path( "rest/layers/" + layerName + ".json").request().put( Entity.entity( "{'layer':{'defaultStyle':{'name':'" + defaultStyle + "'}}}", MediaType.APPLICATION_JSON)); } return getWebTarget().path( "rest/workspaces/" + workspaceName + "/datastores/" + datastoreName + "/featuretypes").request().post( Entity.entity( "{'featureType':{'name':'" + layerName + "'}}", MediaType.APPLICATION_JSON)); } /** * Delete a feature layer from geoserver * * @param layerName * @return */ public Response deleteFeatureLayer( final String layerName ) { return getWebTarget().path( "rest/layers/" + layerName).request().delete(); } /** * Change the default style of a layer * * @param layerName * @param styleName * @return */ public Response setLayerStyle( final String layerName, final String styleName ) { return getWebTarget().path( "rest/layers/" + layerName + ".json").request().put( Entity.entity( "{'layer':{'defaultStyle':{'name':'" + styleName + "'}}}", MediaType.APPLICATION_JSON)); } /** * Get a geoserver style * * @param styleName * @return */ public Response getStyle( @PathParam("styleName") final String styleName ) { final Response resp = getWebTarget().path( "rest/styles/" + styleName + ".sld").request().get(); if (resp.getStatus() == Status.OK.getStatusCode()) { final InputStream inStream = (InputStream) resp.getEntity(); return Response.ok( inStream, MediaType.APPLICATION_XML).header( "Content-Disposition", "attachment; filename=\"" + styleName + ".sld\"").build(); } return resp; } /** * Get a list of geoserver styles * * @return */ public Response getStyles() { final Response resp = getWebTarget().path( "rest/styles.json").request().get(); if (resp.getStatus() == Status.OK.getStatusCode()) { resp.bufferEntity(); // get the style names final JSONArray styleArray = getArrayEntryNames( JSONObject.fromObject(resp.readEntity(String.class)), "styles", "style"); final JSONObject stylesObj = new JSONObject(); stylesObj.put( "styles", styleArray); return Response.ok( stylesObj.toString(defaultIndentation)).build(); } return resp; } /** * Add a style to geoserver * * @param styleName * @param fileInStream * @return */ public Response addStyle( final String styleName, final InputStream fileInStream ) { getWebTarget().path( "rest/styles").request().post( Entity.entity( "{'style':{'name':'" + styleName + "','filename':'" + styleName + ".sld'}}", MediaType.APPLICATION_JSON)); return getWebTarget().path( "rest/styles/" + styleName).request().put( Entity.entity( fileInStream, "application/vnd.ogc.sld+xml")); } /** * Delete a style from geoserver * * @param styleName * @return */ public Response deleteStyle( final String styleName ) { return getWebTarget().path( "rest/styles/" + styleName).request().delete(); } /** * Get coverage store from geoserver * * @param workspaceName * @param coverageName * @return */ public Response getCoverageStore( final String workspaceName, final String coverageName ) { final Response resp = getWebTarget().path( "rest/workspaces/" + workspaceName + "/coveragestores/" + coverageName + ".json").request().get(); if (resp.getStatus() == Status.OK.getStatusCode()) { resp.bufferEntity(); final JSONObject cvgstore = JSONObject.fromObject(resp.readEntity(String.class)); if (cvgstore != null) { return Response.ok( cvgstore.toString(defaultIndentation)).build(); } } return resp; } /** * Get a list of coverage stores from geoserver * * @param workspaceName * @return */ public Response getCoverageStores( final String workspaceName ) { final Response resp = getWebTarget().path( "rest/workspaces/" + workspaceName + "/coveragestores.json").request().get(); if (resp.getStatus() == Status.OK.getStatusCode()) { resp.bufferEntity(); // get the datastore names final JSONArray coveragesArray = getArrayEntryNames( JSONObject.fromObject(resp.readEntity(String.class)), "coverageStores", "coverageStore"); final JSONObject dsObj = new JSONObject(); dsObj.put( "coverageStores", coveragesArray); return Response.ok( dsObj.toString(defaultIndentation)).build(); } return resp; } /** * Add coverage store to geoserver * * @param workspaceName * @param cvgStoreName * @param gwStoreName * @param equalizeHistogramOverride * @param interpolationOverride * @param scaleTo8Bit * @return */ public Response addCoverageStore( final String workspaceName, String cvgStoreName, final String gwStoreName, final Boolean equalizeHistogramOverride, final String interpolationOverride, final Boolean scaleTo8Bit ) { final DataStorePluginOptions inputStoreOptions = getStorePlugin(gwStoreName); if ((cvgStoreName == null) || cvgStoreName.isEmpty()) { cvgStoreName = gwStoreName + GeoServerConfig.DEFAULT_CS; } // Get the store's db config final Map<String, String> storeConfigMap = inputStoreOptions.getOptionsAsMap(); // Add in geoserver coverage store info storeConfigMap.put( GEOSERVER_WORKSPACE, workspaceName); storeConfigMap.put( "gwNamespace", inputStoreOptions.getGeowaveNamespace()); storeConfigMap.put( GEOSERVER_CS, cvgStoreName); final String cvgStoreXml = createCoverageXml( storeConfigMap, equalizeHistogramOverride, interpolationOverride, scaleTo8Bit); System.out.println("Add coverage store - xml params:\n" + cvgStoreXml); // create a new geoserver style return getWebTarget().path( "rest/workspaces/" + workspaceName + "/coveragestores").request().post( Entity.entity( cvgStoreXml, MediaType.APPLICATION_XML)); } /** * Delete coverage store form geoserver * * @param workspaceName * @param cvgstoreName * @return */ public Response deleteCoverageStore( final String workspaceName, final String cvgstoreName ) { return getWebTarget().path( "rest/workspaces/" + workspaceName + "/coveragestores/" + cvgstoreName).queryParam( "recurse", "true").request().delete(); } /** * Get a list of coverages (raster layers) from geoserver * * @param workspaceName * @param cvsstoreName * @return */ public Response getCoverages( final String workspaceName, final String cvsstoreName ) { final Response resp = getWebTarget() .path( "rest/workspaces/" + workspaceName + "/coveragestores/" + cvsstoreName + "/coverages.json") .request() .get(); if (resp.getStatus() == Status.OK.getStatusCode()) { resp.bufferEntity(); // get the datastore names final JSONArray coveragesArray = getArrayEntryNames( JSONObject.fromObject(resp.readEntity(String.class)), "coverages", "coverage"); final JSONObject dsObj = new JSONObject(); dsObj.put( "coverages", coveragesArray); return Response.ok( dsObj.toString(defaultIndentation)).build(); } return resp; } /** * Get coverage from geoserver * * @param workspaceName * @param cvgStoreName * @param coverageName * @return */ public Response getCoverage( final String workspaceName, final String cvgStoreName, final String coverageName ) { final Response resp = getWebTarget() .path( "rest/workspaces/" + workspaceName + "/coveragestores/" + cvgStoreName + "/coverages/" + coverageName + ".json") .request() .get(); if (resp.getStatus() == Status.OK.getStatusCode()) { resp.bufferEntity(); final JSONObject cvg = JSONObject.fromObject(resp.readEntity(String.class)); if (cvg != null) { return Response.ok( cvg.toString(defaultIndentation)).build(); } } return resp; } /** * Add coverage to geoserver * * @param workspaceName * @param cvgStoreName * @param coverageName * @return */ public Response addCoverage( final String workspaceName, final String cvgStoreName, final String coverageName ) { final String jsonString = "{'coverage':" + "{'name':'" + coverageName + "'," + "'nativeCoverageName':'" + coverageName + "'}}"; LOGGER.debug("Posting JSON: " + jsonString + " to " + workspaceName + "/" + cvgStoreName); return getWebTarget().path( "rest/workspaces/" + workspaceName + "/coveragestores/" + cvgStoreName + "/coverages").request().post( Entity.entity( jsonString, MediaType.APPLICATION_JSON)); } /** * Delete coverage from geoserver * * @param workspaceName * @param cvgstoreName * @param coverageName * @return */ public Response deleteCoverage( final String workspaceName, final String cvgstoreName, final String coverageName ) { return getWebTarget() .path( "rest/workspaces/" + workspaceName + "/coveragestores/" + cvgstoreName + "/coverages/" + coverageName) .queryParam( "recurse", "true") .request() .delete(); } // Internal methods protected String createFeatureTypeJson( final String featureTypeName ) { final JSONObject featTypeJson = new JSONObject(); featTypeJson.put( "name", featureTypeName); final JSONObject jsonObj = new JSONObject(); jsonObj.put( "featureType", featTypeJson); return jsonObj.toString(); } protected JSONArray getArrayEntryNames( JSONObject jsonObj, final String firstKey, final String secondKey ) { // get the top level object/array if (jsonObj.get(firstKey) instanceof JSONObject) { jsonObj = jsonObj.getJSONObject(firstKey); } else if (jsonObj.get(firstKey) instanceof JSONArray) { final JSONArray tempArray = jsonObj.getJSONArray(firstKey); if (tempArray.size() > 0) { if (tempArray.get(0) instanceof JSONObject) { jsonObj = tempArray.getJSONObject(0); } else { // empty list! return new JSONArray(); } } } // get the sub level object/array final JSONArray entryArray = new JSONArray(); if (jsonObj.get(secondKey) instanceof JSONObject) { final JSONObject entry = new JSONObject(); entry.put( "name", jsonObj.getJSONObject( secondKey).getString( "name")); entryArray.add(entry); } else if (jsonObj.get(secondKey) instanceof JSONArray) { final JSONArray entries = jsonObj.getJSONArray(secondKey); for (int i = 0; i < entries.size(); i++) { final JSONObject entry = new JSONObject(); entry.put( "name", entries.getJSONObject( i).getString( "name")); entryArray.add(entry); } } return entryArray; } protected String createDatastoreJson( final String geowaveStoreType, final Map<String, String> geowaveStoreConfig, final String name, final String lockMgmt, final String authMgmtProvider, final String authDataUrl, final String queryIndexStrategy, final boolean enabled ) { final JSONObject dataStore = new JSONObject(); dataStore.put( "name", name); dataStore.put( "type", GeoServerConfig.DISPLAY_NAME_PREFIX + geowaveStoreType); dataStore.put( "enabled", Boolean.toString(enabled)); final JSONObject connParams = new JSONObject(); if (geowaveStoreConfig != null) { for (final Entry<String, String> e : geowaveStoreConfig.entrySet()) { connParams.put( e.getKey(), e.getValue()); } } connParams.put( "Lock Management", lockMgmt); connParams.put( GeoServerConfig.QUERY_INDEX_STRATEGY_KEY, queryIndexStrategy); connParams.put( "Authorization Management Provider", authMgmtProvider); if (!authMgmtProvider.equals("empty")) { connParams.put( "Authorization Data URL", authDataUrl); } dataStore.put( "connectionParameters", connParams); final JSONObject jsonObj = new JSONObject(); jsonObj.put( "dataStore", dataStore); return jsonObj.toString(); } private String createCoverageXml( final Map<String, String> geowaveStoreConfig, final Boolean equalizeHistogramOverride, final String interpolationOverride, final Boolean scaleTo8Bit ) { String coverageXml = null; final String workspace = geowaveStoreConfig.get(GEOSERVER_WORKSPACE); final String cvgstoreName = geowaveStoreConfig.get(GEOSERVER_CS); StreamResult result = null; try { // create the post XML final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setFeature( "http://xml.org/sax/features/external-general-entities", false); factory.setFeature( "http://xml.org/sax/features/external-parameter-entities", false); final Document xmlDoc = factory.newDocumentBuilder().newDocument(); final Element rootEl = xmlDoc.createElement("coverageStore"); xmlDoc.appendChild(rootEl); final Element nameEl = xmlDoc.createElement("name"); nameEl.appendChild(xmlDoc.createTextNode(cvgstoreName)); rootEl.appendChild(nameEl); final Element wsEl = xmlDoc.createElement("workspace"); wsEl.appendChild(xmlDoc.createTextNode(workspace)); rootEl.appendChild(wsEl); final Element typeEl = xmlDoc.createElement("type"); typeEl.appendChild(xmlDoc.createTextNode("GeoWaveRasterFormat")); rootEl.appendChild(typeEl); final Element enabledEl = xmlDoc.createElement("enabled"); enabledEl.appendChild(xmlDoc.createTextNode("true")); rootEl.appendChild(enabledEl); final Element configEl = xmlDoc.createElement("configure"); configEl.appendChild(xmlDoc.createTextNode("all")); rootEl.appendChild(configEl); // Method using custom URL & handler: final String storeConfigUrl = createParamUrl( geowaveStoreConfig, equalizeHistogramOverride, interpolationOverride, scaleTo8Bit); final Element urlEl = xmlDoc.createElement("url"); urlEl.appendChild(xmlDoc.createTextNode(storeConfigUrl)); rootEl.appendChild(urlEl); // use a transformer to create the xml string for the rest call final TransformerFactory xformerFactory = TransformerFactory.newInstance(); // HP Fortify "XML External Entity Injection" false positive // The following modifications to xformerFactory are the // fortify-recommended procedure to secure a TransformerFactory // but the report still flags this instance xformerFactory.setFeature( XMLConstants.FEATURE_SECURE_PROCESSING, true); final Transformer xformer = xformerFactory.newTransformer(); final DOMSource source = new DOMSource( xmlDoc); result = new StreamResult( new StringWriter()); xformer.transform( source, result); coverageXml = result.getWriter().toString(); } catch (final TransformerException e) { LOGGER.error( "Unable to create transformer", e); } catch (final ParserConfigurationException e1) { LOGGER.error( "Unable to create DocumentBuilderFactory", e1); } finally { if ((result != null) && (result.getWriter() != null)) { try { result.getWriter().close(); } catch (final IOException e) { LOGGER.error( e.getLocalizedMessage(), e); } } } return coverageXml; } private String createParamUrl( final Map<String, String> geowaveStoreConfig, final Boolean equalizeHistogramOverride, final String interpolationOverride, final Boolean scaleTo8Bit ) { // Retrieve store config final String user = geowaveStoreConfig.get("user"); final String pass = geowaveStoreConfig.get("password"); final String zookeeper = geowaveStoreConfig.get("zookeeper"); final String instance = geowaveStoreConfig.get("instance"); final String gwNamespace = geowaveStoreConfig.get("gwNamespace"); // Create the custom geowave url w/ params final StringBuffer buf = new StringBuffer(); buf.append("user="); buf.append(user); buf.append(";password="); buf.append(pass); buf.append(";zookeeper="); buf.append(zookeeper); buf.append(";instance="); buf.append(instance); buf.append(";gwNamespace="); buf.append(gwNamespace); if (equalizeHistogramOverride != null) { buf.append(";equalizeHistogramOverride="); buf.append(equalizeHistogramOverride); } if (interpolationOverride != null) { buf.append(";interpolationOverride="); buf.append(interpolationOverride); } if (scaleTo8Bit != null) { buf.append(";scaleTo8Bit="); buf.append(scaleTo8Bit); } return buf.toString(); } public DataStorePluginOptions getStorePlugin( final String storeName ) { final StoreLoader inputStoreLoader = new StoreLoader( storeName); if (!inputStoreLoader.loadFromConfig(config.getPropFile())) { throw new ParameterException( "Cannot find store name: " + inputStoreLoader.getStoreName()); } return inputStoreLoader.getDataStorePlugin(); } public ArrayList<String> getStoreAdapters( final String storeName, final String adapterId ) { final ArrayList<DataAdapterInfo> adapterInfoList = getStoreAdapterInfo( storeName, adapterId); final ArrayList<String> adapterIdList = new ArrayList<String>(); for (final DataAdapterInfo info : adapterInfoList) { adapterIdList.add(info.adapterId); } return adapterIdList; } private ArrayList<DataAdapterInfo> getStoreAdapterInfo( final String storeName, final String adapterId ) { final DataStorePluginOptions dsPlugin = getStorePlugin(storeName); final AdapterStore adapterStore = dsPlugin.createAdapterStore(); final ArrayList<DataAdapterInfo> adapterInfoList = new ArrayList<DataAdapterInfo>(); LOGGER.debug("Adapter list for " + storeName + " with adapterId = " + adapterId + ": "); try (final CloseableIterator<DataAdapter<?>> it = adapterStore.getAdapters()) { while (it.hasNext()) { final DataAdapter<?> adapter = it.next(); final DataAdapterInfo info = getAdapterInfo( adapterId, adapter); if (info != null) { adapterInfoList.add(info); LOGGER.debug("> '" + info.adapterId + "' adapter passed filter"); } } } catch (final IOException e) { LOGGER.error( "Unable to close adapter iterator while looking up coverage names", e); } LOGGER.debug("getStoreAdapterInfo(" + storeName + ") got " + adapterInfoList.size() + " ids"); return adapterInfoList; } private DataAdapterInfo getAdapterInfo( final String adapterId, final DataAdapter adapter ) { LOGGER.debug("getAdapterInfo for id = " + adapterId); final DataAdapterInfo info = new DataAdapterInfo(); info.adapterId = adapter.getAdapterId().getString(); info.isRaster = false; if (adapter instanceof RasterDataAdapter) { info.isRaster = true; } LOGGER.debug("> Adapter ID: " + info.adapterId); LOGGER.debug("> Adapter Type: " + adapter.getClass().getSimpleName()); if ((adapterId == null) || adapterId.equals(AddOption.ALL.name())) { LOGGER.debug("id is null or all"); return info; } if (adapterId.equals(adapter.getAdapterId().getString())) { LOGGER.debug("id matches adapter id"); return info; } if (adapterId.equals(AddOption.RASTER.name()) && (adapter instanceof RasterDataAdapter)) { LOGGER.debug("id is all-raster and adapter is raster type"); return info; } if (adapterId.equals(AddOption.VECTOR.name()) && (adapter instanceof GeotoolsFeatureDataAdapter)) { LOGGER.debug("id is all-vector and adapter is vector type"); return info; } LOGGER.debug("No match!"); return null; } }