package org.gcube.application.cms.concessioni.sdi; import it.geosolutions.geoserver.rest.GeoServerRESTPublisher; import it.geosolutions.geoserver.rest.GeoServerRESTReader; import it.geosolutions.geoserver.rest.decoder.RESTFeatureType; import it.geosolutions.geoserver.rest.decoder.RESTLayer; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.gcube.application.cms.concessioni.sdi.faults.SDIInteractionException; import org.gcube.application.cms.plugins.requests.BaseExecutionRequest; import org.gcube.application.cms.serialization.Serialization; import org.gcube.application.geoportal.common.model.document.filesets.GCubeSDILayer; import org.gcube.application.geoportal.common.model.document.filesets.RegisteredFile; import org.gcube.application.geoportal.common.model.document.filesets.RegisteredFileSet; import org.gcube.application.geoportal.common.utils.Files; import org.gcube.data.transfer.library.DataTransferClient; import org.gcube.data.transfer.library.TransferResult; import org.gcube.data.transfer.model.Destination; import org.gcube.data.transfer.model.DestinationClashPolicy; import org.gcube.spatial.data.gis.GISInterface; import org.gcube.spatial.data.gis.is.AbstractGeoServerDescriptor; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; @Slf4j public class SDIManager { static private final String EPSG_4326="EPSG:4326"; static private final String WGS84_FULL="GEOGCS[\"WGS 84\", DATUM[\"World Geodetic System 1984\", SPHEROID[\"WGS 84\", 6378137.0, 298.257223563, AUTHORITY[\"EPSG\",\"7030\"]],"+ "AUTHORITY[\"EPSG\",\"6326\"]], PRIMEM[\"Greenwich\", 0.0, AUTHORITY[\"EPSG\",\"8901\"]], UNIT[\"degree\", 0.017453292519943295],"+ "AXIS[\"Geodetic longitude\", EAST], AXIS[\"Geodetic latitude\", NORTH], AUTHORITY[\"EPSG\",\"4326\"]]"; public static final Pattern HOSTNAME_PATTERN=Pattern.compile("(?<=\\:\\/\\/)[^\\:]*"); public static final Pattern PORT_PATTERN=Pattern.compile("(?<=\\:)[\\d]+"); public static final Pattern DB_NAME_PATTERN=Pattern.compile("(?<=\\/)[^\\/]*(?=$)"); private final GISInterface gis; @Getter private final DataTransferClient dtGeoServer; @Getter private final String geoserverHostName; @Getter private final AbstractGeoServerDescriptor currentGeoserver; public SDIManager() throws SDIInteractionException { try{ log.debug("Initializing GIS Interface.."); gis=GISInterface.get(); currentGeoserver=gis.getCurrentGeoServer(); if(currentGeoserver==null) throw new Exception("Unable to contact data transfer for geoserver "); log.debug("Found geoserver descriptor "+currentGeoserver); geoserverHostName=new URL(currentGeoserver.getUrl()).getHost(); log.debug("Contacting Data Transfer from geoserver {} ",geoserverHostName); dtGeoServer=DataTransferClient.getInstanceByEndpoint("https://"+geoserverHostName); if(!currentGeoserver.getReader().existGeoserver()) throw new Exception("Geoserver not reachable"); }catch(Exception e) { throw new SDIInteractionException("Unable to initialize SDI Manager",e); } } /** * Expected paramters : * - "workspace" * - "layerTitle" * - "documentID" * - "basePersistencePath" (profile specific, e.g. "GNA") * * @param fileSet * @param params * @return * @throws SDIInteractionException */ public RegisteredFileSet materializeLayer(RegisteredFileSet fileSet, Document params) throws SDIInteractionException{ try { log.debug("Materializing FS {} on {} ", fileSet, geoserverHostName); // validate parameters String workspace = BaseExecutionRequest.getMandatory("workspace", params); String documentID = BaseExecutionRequest.getMandatory("documentID", params); String basePersistencePAth = BaseExecutionRequest.getMandatory("basePersistencePath", params); // check if empty if (fileSet.getPayloads().isEmpty()) throw new SDIInteractionException("No payload to materialize"); Document geoserverInfo = new Document(); geoserverInfo.put("_type", "Geoserver"); geoserverInfo.put("workspace", workspace); // Evaluate Layer Name String baseName = getToUseBaseLayerName(fileSet); log.debug("Base layer name is {}, checking conflicts.. "); String toSetLayerName = baseName; //Check if layer already exists int count = 0; GeoServerRESTReader gsReader = currentGeoserver.getReader(); while (gsReader.getLayer(workspace, toSetLayerName) != null) { count++; toSetLayerName = baseName + "_" + count; log.debug("layer for " + baseName + " already existing, trying " + toSetLayerName); } geoserverInfo.put("layerName", toSetLayerName); log.debug("Layer name will be {} ", toSetLayerName); String folderRelativePath = basePersistencePAth + "/" + documentID + "/" + fileSet.getUUID() + "/" + toSetLayerName; log.debug("GS Relative destination path is {} ", folderRelativePath); geoserverInfo.put("persistencePath", folderRelativePath); List filenames = new ArrayList<>(); String absolutePath = null; for (Object o : fileSet.getPayloads()) { RegisteredFile file = Serialization.convert(o, RegisteredFile.class); log.info("Sending {} to GS {} at {} ", file, geoserverHostName, folderRelativePath); String completeFilename = Files.fixFilename(file.getName()); completeFilename = completeFilename.replaceAll(baseName, toSetLayerName); Destination destination = new Destination(completeFilename); destination.setCreateSubfolders(true); destination.setOnExistingFileName(DestinationClashPolicy.REWRITE); destination.setOnExistingSubFolder(DestinationClashPolicy.APPEND); destination.setPersistenceId("geoserver"); destination.setSubFolder(folderRelativePath); log.debug("Sending {} to {}", file, destination); TransferResult result = getDtGeoServer().httpSource(new URL(file.getLink()), destination); log.debug("Transferred " + result); filenames.add(completeFilename); // NB Clash con subfolder is APPEND, thus FOLDER is expected to be the one specified by caller //geoserverInfo.put(""result.getRemotePath().substring(0, result.getRemotePath().lastIndexOf("/"))); absolutePath = result.getRemotePath().substring(0, result.getRemotePath().lastIndexOf("/")); } geoserverInfo.put("files", filenames); // Publishing layer in GS String storeName = toSetLayerName + "_store"; geoserverInfo.put("storeName", storeName); GeoServerRESTPublisher publisher = currentGeoserver.getPublisher(); log.debug("Trying to create remote workspace : " + workspace); createWorkspace(workspace); log.debug("Publishing remote folder " + absolutePath); URL directoryPath = new URL("file:" + absolutePath + "/" + toSetLayerName + ".shp"); //TODO Evaluate SRS boolean published = publisher.publishShp( workspace, storeName, null, toSetLayerName, // UploadMethod.FILE, // neeeds zip GeoServerRESTPublisher.UploadMethod.EXTERNAL, // needs shp directoryPath.toURI(), EPSG_4326, //SRS ""); // default style if (!published) { throw new SDIInteractionException("Unable to publish layer " + toSetLayerName + " under " + workspace + ". Unknown Geoserver fault."); } RESTLayer l = gsReader.getLayer(workspace, toSetLayerName); RESTFeatureType f = gsReader.getFeatureType(l); /*https://geoserver1.dev.d4science.org/geoserver/gna_conc_18/wms? service=WMS&version=1.1.0&request=GetMap&layers=gna_conc_18:pos& styles=&bbox=8.62091913167495,40.62975046683799,8.621178639172953,40.630257904721645& width=392&height=768&srs=EPSG:4326&format=application/openlayers */ List ogcLinks = new ArrayList<>(); Document wmsLink = new Document(); wmsLink.put("wms", String.format("https://%1$s/geoserver/%2$s/wms?" + "service=WMS&version=1.1.0&request=GetMap&layers=%2$s:%3$s&" + "styles=&bbox=%4$f,%5$f,%6$f,%7$f&srs=%8$s&format=application/openlayers&width=%9$d&height=%10$d", geoserverHostName, workspace, toSetLayerName, f.getMinX(), f.getMinY(), f.getMaxX(), f.getMaxY(), EPSG_4326, 400, 400)); ogcLinks.add(wmsLink); List platformInfo = new ArrayList<>(); platformInfo.add(geoserverInfo); // TODO Metadata // Materialization object GCubeSDILayer materialization = new GCubeSDILayer(); materialization.put(GCubeSDILayer.OGC_LINKS, ogcLinks); materialization.put(GCubeSDILayer.B_BOX, new GCubeSDILayer.BBOX(f.getMaxX(), f.getMaxY(), f.getMinX(), f.getMinY())); materialization.put(GCubeSDILayer.PLATFORM_INFO, platformInfo); log.info("Generated Materialization {}", materialization); //Add Materialization to registered file set List materializations = fileSet.getMaterializations(); if (materializations == null) materializations = new ArrayList(); materializations.add(materialization); fileSet.put(RegisteredFileSet.MATERIALIZATIONS, materializations); return fileSet; }catch(SDIInteractionException e){ throw e; }catch (Throwable t){ throw new SDIInteractionException("Unexpected exception while trying to materialize File Set "+t.getMessage(),t); } } public String createWorkspace(String toCreate) throws SDIInteractionException { try { if(!currentGeoserver.getReader().getWorkspaceNames().contains(toCreate)) { log.debug("Creating workspace : "+toCreate); if(!currentGeoserver.getPublisher().createWorkspace(toCreate)) throw new SDIInteractionException("Unable to create workspace "+toCreate); }else log.debug("Workspace "+toCreate+" exists."); return toCreate; } catch (IllegalArgumentException | MalformedURLException e) { throw new SDIInteractionException("Unable to create workspace "+toCreate,e); } } private static final String getToUseBaseLayerName(RegisteredFileSet fileset){ // ******** IDENTIFY LAYER NAME correct layer name // Must be unique under same WS // equal to shp base name String baseName= ""; // Chose layer name first identifying filename for(Object p:fileset.getPayloads()){ RegisteredFile f = Serialization.convert(p,RegisteredFile.class); String name=f.getName(); if(name.endsWith(".shp")) { log.debug("SHP is {}",name); baseName= Files.fixFilename(name.substring(0,name.lastIndexOf('.'))); break; } } return baseName; } }