/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.constellation.rest.api; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.inject.Inject; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.metadata.iso.DefaultMetadata; import org.apache.sis.storage.DataStore; import org.apache.sis.storage.DataStoreException; import org.apache.sis.util.logging.Logging; import org.constellation.business.IDatasetBusiness; import org.constellation.business.IProviderBusiness; import org.constellation.configuration.AcknowlegementType; import org.constellation.configuration.ConfigurationException; import org.constellation.configuration.DataBrief; import org.constellation.configuration.ProviderConfiguration; import org.constellation.dto.DataDescription; import org.constellation.dto.ParameterValues; import org.constellation.dto.SimpleValue; import org.constellation.provider.Data; import org.constellation.provider.DataProvider; import org.constellation.provider.DataProviders; import org.constellation.ws.CstlServiceException; import org.constellation.ws.rs.LayerProviders; import org.geotoolkit.coverage.GridSampleDimension; import org.geotoolkit.coverage.io.CoverageStoreException; import org.geotoolkit.coverage.io.GridCoverageReader; import org.geotoolkit.data.FeatureStore; import org.geotoolkit.data.memory.ExtendedFeatureStore; import org.geotoolkit.io.wkt.PrjFiles; import org.geotoolkit.referencing.CRS; import org.geotoolkit.storage.DataFileStore; import org.geotoolkit.storage.coverage.CoverageReference; import org.opengis.referencing.crs.CRSAuthorityFactory; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.ImageCRS; import org.opengis.util.FactoryException; import org.opengis.util.GenericName; import org.springframework.stereotype.Component; /** * RestFull API for provider management/operations. * * @author Fabien Bernard (Geomatys). * @version 0.9 * @since 0.9 */ @Component @Path("/1/domain/{domainId}/provider") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public class ProviderRest { private static final Logger LOGGER = Logging.getLogger("org.constellation.rest.api"); @Inject private IProviderBusiness providerBusiness; @Inject private IDatasetBusiness datasetBusiness; @Inject private LayerProviders layerProviders; @POST @Path("/{id}/test") public Response test( final @PathParam("domainId") int domainId, final @PathParam("id") String providerIdentifier, final ProviderConfiguration configuration) { try { final Set<GenericName> names = providerBusiness.test(providerIdentifier, configuration); if (names.isEmpty()){ LOGGER.warning("non data found for provider: " + providerIdentifier); return Response.status(500).entity("Unable to find any data, please check the database parameters and make sure that the database is properly configured.").build(); } } catch (DataStoreException | ConfigurationException e) { LOGGER.log(Level.WARNING, "Cannot open provider "+providerIdentifier, e); return Response.status(500).entity(e.getMessage()).build(); } return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build(); } @PUT @Path("/{id}") public Response update(final @PathParam("domainId") int domainId, final @PathParam("id") String id, final ProviderConfiguration config) { try { providerBusiness.update(id, config); }catch(ConfigurationException ex){ LOGGER.log(Level.WARNING, ex.getMessage(), ex); return Response.status(500).entity(ex.getMessage()).build(); } return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build(); } /** * Create a new provider from the given configuration. */ @POST @Path("/{id}") public Response create(final @PathParam("domainId") int domainId, final @PathParam("id") String id, final ProviderConfiguration config) { try { providerBusiness.create(id, config); } catch (Exception ex) { LOGGER.log(Level.WARNING, ex.getLocalizedMessage(), ex); return Response.status(500).entity(ex.getLocalizedMessage()).build(); } return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build(); } @GET @Path("/{id}/epsgCode") public Response getAllEpsgCode(final @PathParam("domainId") int domainId, final @PathParam("id") String providerIdentifier) throws FactoryException { final CRSAuthorityFactory factory = CRS.getAuthorityFactory(Boolean.FALSE); final Set<String> authorityCodes = factory.getAuthorityCodes(CoordinateReferenceSystem.class); List<String> codes = new ArrayList<>(); for (String code : authorityCodes){ code += " - " + factory.getDescriptionText(code).toString(); codes.add(code); } return Response.ok().entity(codes).build(); } @POST @Path("/{id}/createprj") public Response createPrj(final @PathParam("domainId") int domainId, final @PathParam("id") String providerIdentifier, final Map<String,String> epsgCode) throws DataStoreException, FactoryException, IOException { final DataProvider provider = DataProviders.getInstance().getProvider(providerIdentifier); final DataStore datastore = provider.getMainStore(); if (datastore instanceof DataFileStore){ proceedToCreatePrj(provider,(DataFileStore)datastore,epsgCode); return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build(); }else if(datastore instanceof ExtendedFeatureStore) { final ExtendedFeatureStore efs = (ExtendedFeatureStore) datastore; final FeatureStore fstore = efs.getWrapped(); if(fstore instanceof DataFileStore) { proceedToCreatePrj(provider,(DataFileStore)fstore,epsgCode); return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build(); } } return Response.status(500).entity("Cannot creates the prj file for the data. the operation is not implemented yet for this format!").build(); } private void proceedToCreatePrj(final DataProvider provider, final DataFileStore dataFileStore, final Map<String,String> epsgCode) throws DataStoreException,FactoryException,IOException { File[] dataFiles = dataFileStore.getDataFiles(); if(dataFiles == null) return; if (dataFiles.length == 1 && dataFiles[0].isDirectory()){ dataFiles = dataFiles[0].listFiles(); } if(dataFiles == null || dataFiles.length==0) return; final String firstFileName = dataFiles[0].getName(); final String fileNameWithoutExtention; if(firstFileName.indexOf('.')!=-1){ fileNameWithoutExtention = firstFileName.substring(0, firstFileName.indexOf('.')); }else { fileNameWithoutExtention = firstFileName; } final String parentPath = dataFiles[0].getParentFile().getAbsolutePath(); final CoordinateReferenceSystem coordinateReferenceSystem = CRS.decode(epsgCode.get("codeEpsg")); PrjFiles.write(coordinateReferenceSystem, new File(parentPath+File.separator+fileNameWithoutExtention+".prj")); provider.reload(); } /** * Create a new provider from the given configuration. */ @GET @Path("/{id}/crs") public Response verifyCRS(final @PathParam("domainId") int domainId, final @PathParam("id") String providerIdentifier){ try { final HashMap<GenericName, CoordinateReferenceSystem> nameCoordinateReferenceSystemHashMap = DataProviders.getInstance().getCRS(providerIdentifier); for( CoordinateReferenceSystem crs : nameCoordinateReferenceSystemHashMap.values()){ if (crs == null || crs instanceof ImageCRS){ return Response.status(500).build(); } } return Response.ok(true).build(); } catch (DataStoreException e) { LOGGER.log(Level.WARNING, "Cannot get CRS for provider "+providerIdentifier, e); return Response.status(500).build(); } } /** * Delete a provider with the given id. * @param id */ @DELETE @Path("{id}") public Response delete(final @PathParam("id") String id) { final DataProvider old = DataProviders.getInstance().getProvider(id); try { DataProviders.getInstance().removeProvider(old); } catch (ConfigurationException ex) { LOGGER.log(Level.WARNING, null, ex); return Response.status(500).build(); } return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build(); } /** * @see LayerProviders#getDataDescription(String, String) */ @POST @Path("dataDescription") public Response dataDescription(final ParameterValues values) { try { final String id = values.getValues().get("providerId"); final String layerName = values.getValues().get("dataId"); final DataDescription result = layerProviders.getDataDescription(id, layerName); return Response.ok(result).build(); } catch (Exception ex) { LOGGER.log(Level.WARNING, ex.getLocalizedMessage(), ex); return Response.status(500).entity(new AcknowlegementType("Failure", ex.getLocalizedMessage())).build(); } } /** * @see LayerProviders#getDataGeographicExtent(String, String) */ @POST @Path("dataGeographicExtent") public Response dataGeographicExtent(final ParameterValues values) { try { final String id = values.getValues().get("providerId"); final String layerName = values.getValues().get("dataId"); return Response.ok(layerProviders.getDataGeographicExtent(id, layerName)).build(); } catch (Exception ex) { LOGGER.log(Level.WARNING, ex.getLocalizedMessage(), ex); return Response.status(500).entity(new AcknowlegementType("Failure", ex.getLocalizedMessage())).build(); } } @POST @Path("mergedDataGeographicExtent") public Response mergedDataGeographicExtent(final List<DataBrief> briefs) { final Map<String,double[]> result = new HashMap<>(); if(briefs == null || briefs.isEmpty()){ return Response.ok(result).build(); } GeneralEnvelope globalEnv = null; for(final DataBrief db : briefs){ final String id = db.getProvider(); final String layerName; if (db.getNamespace()!=null) { layerName = '{' + db.getNamespace() + '}' + db.getName(); } else { layerName = db.getName(); } try { final DataDescription ddesc = layerProviders.getDataGeographicExtent(id, layerName); final double[] bbox = ddesc.getBoundingBox(); final GeneralEnvelope dataEnv = new GeneralEnvelope(CRS.decode("CRS:84")); dataEnv.setRange(0,bbox[0],bbox[2]); dataEnv.setRange(1,bbox[1],bbox[3]); if(globalEnv == null) { globalEnv = dataEnv; }else { globalEnv.add(dataEnv); } }catch(Exception ex){ LOGGER.log(Level.WARNING, ex.getLocalizedMessage(), ex); } } if(globalEnv != null){ double[] bbox = new double[4]; bbox[0]=globalEnv.getMinimum(0); bbox[1]=globalEnv.getMinimum(1); bbox[2]=globalEnv.getMaximum(0); bbox[3]=globalEnv.getMaximum(1); result.put("boundingBox",bbox); } return Response.ok(result).build(); } /** * @see LayerProviders#getPropertyValues(String, String, String) */ @GET @Path("{id}/{layerName}/{property}/propertyValues") public Response propertyValues(final @PathParam("domainId") int domainId, final @PathParam("id") String id, final @PathParam("layerName") String layerName, final @PathParam("property") String property) { try { return Response.ok(LayerProviders.getPropertyValues(id, layerName, property)).build(); } catch (CstlServiceException ex) { LOGGER.log(Level.WARNING, "Cannot retrieve information for layer "+layerName, ex); return Response.status(500).entity(new AcknowlegementType("Failure", ex.getLocalizedMessage())).build(); } } /** * Indicate if given provider contains a geophysic data. */ @GET @Path("{id}/{layerName}/isGeophysic") public Response isGeophysic(final @PathParam("domainId") int domainId, final @PathParam("id") String id, final @PathParam("layerName") String layerName) { boolean isGeophysic = false; try { final Data data = LayerProviders.getLayer(id, layerName); if(data!=null && data.getOrigin() instanceof CoverageReference){ final CoverageReference ref = (CoverageReference) data.getOrigin(); final GridCoverageReader reader = ref.acquireReader(); final List<GridSampleDimension> dims = reader.getSampleDimensions(ref.getImageIndex()); if(dims!=null && !dims.isEmpty()){ isGeophysic = true; } ref.recycle(reader); } } catch (CstlServiceException|CoverageStoreException ex) { LOGGER.log(Level.WARNING, "Cannot retrieve information for layer "+layerName, ex); return Response.status(500).entity(new AcknowlegementType("Failure", ex.getLocalizedMessage())).build(); } return Response.ok(new SimpleValue(isGeophysic)).build(); } /** * List the available pyramids for this layer */ @GET @Path("{id}/{layerName}/listPyramidChoice") public Response listPyramids(final @PathParam("domainId") int domainId, final @PathParam("id") String id, final @PathParam("layerName") String layerName) { try { return Response.ok(providerBusiness.listPyramids(id, layerName)).build(); } catch (DataStoreException ex) { LOGGER.log(Level.WARNING, "Cannot retrieve information for layer "+layerName, ex); return Response.status(500).entity(new AcknowlegementType("Failure", ex.getLocalizedMessage())).build(); } } /** * @see LayerProviders#getBandValues(String, String, int) */ @GET @Path("{id}/{layerName}/{bandIndex}/bandValues") public Response bandValues(final @PathParam("domainId") int domainId, final @PathParam("id") String id, final @PathParam("layerName") String layerName, final @PathParam("bandIndex") int bandIndex) { try { return Response.ok(LayerProviders.getBandValues(id, layerName, bandIndex)).build(); } catch (CstlServiceException ex) { LOGGER.log(Level.WARNING, "Cannot retrieve information for layer "+layerName, ex); return Response.status(500).entity(new AcknowlegementType("Failure", ex.getLocalizedMessage())).build(); } } /** * * Is this method still used ?? * * No longer metadata for provider but for dataset */ @GET @Path("metadata/{providerId}") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getMetadata(final @PathParam("domainId") int domainId, final @PathParam("providerId") String providerId) { // for now assume that providerID == datasetID try { return Response.ok(datasetBusiness.getMetadata(providerId)).build(); } catch (Exception e) { LOGGER.log(Level.WARNING, "Cannot retrieve metadata for provider "+providerId, e); return Response.status(500).entity(new AcknowlegementType("Failure", e.getLocalizedMessage())).build(); } } /** * * Is this method still used ?? * * No longer metadata for provider but for dataset */ @POST @Path("metadata/{providerId}") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response setMetadata(final @PathParam("domainId") int domainId, final @PathParam("providerId") String providerId, final DefaultMetadata metadata) { // for now assume that providerID == datasetID try { datasetBusiness.updateMetadata(providerId, metadata); return Response.ok().type(MediaType.TEXT_PLAIN_TYPE).build(); } catch (ConfigurationException e) { LOGGER.log(Level.WARNING, "Cannot update metadata for provider "+providerId, e); return Response.status(500).entity(new AcknowlegementType("Failure", e.getLocalizedMessage())).build(); } } }