import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
import javax.xml.namespace.QName;
import org.argeo.app.api.EntityType;
import org.argeo.app.api.WGS84PosName;
import org.argeo.app.api.geo.FeatureAdapter;
+import org.argeo.app.api.geo.WfsKvp;
import org.argeo.app.geo.CqlUtils;
import org.argeo.app.geo.GeoJson;
import org.argeo.app.geo.GeoUtils;
import org.argeo.app.geo.GpxUtils;
import org.argeo.app.geo.JTS;
+import org.argeo.app.geo.acr.GeoEntityUtils;
import org.argeo.cms.acr.json.AcrJsonUtils;
+import org.argeo.cms.auth.RemoteAuthUtils;
import org.argeo.cms.http.HttpHeader;
+import org.argeo.cms.http.RemoteAuthHttpExchange;
import org.argeo.cms.http.server.HttpServerUtils;
import org.argeo.cms.util.LangUtils;
+import org.geotools.api.feature.GeometryAttribute;
+import org.geotools.api.feature.simple.SimpleFeature;
+import org.geotools.api.feature.simple.SimpleFeatureType;
+import org.geotools.api.feature.type.AttributeDescriptor;
+import org.geotools.api.feature.type.Name;
+import org.geotools.api.referencing.FactoryException;
+import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
+import org.geotools.api.referencing.operation.MathTransform;
+import org.geotools.api.referencing.operation.TransformException;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.NameImpl;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
-import org.opengis.feature.GeometryAttribute;
-import org.opengis.feature.simple.SimpleFeature;
-import org.opengis.feature.simple.SimpleFeatureType;
-import org.opengis.feature.type.AttributeDescriptor;
-import org.opengis.feature.type.Name;
-import org.opengis.referencing.FactoryException;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.TransformException;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
private final static CmsLog log = CmsLog.getLog(WfsHttpHandler.class);
private ProvidedRepository contentRepository;
- // HTTP parameters
- final static String OUTPUT_FORMAT = "outputFormat";
- final static String TYPE_NAMES = "typeNames";
- final static String CQL_FILTER = "cql_filter";
- final static String BBOX = "bbox";
-
private final Map<QName, FeatureAdapter> featureAdapters = new HashMap<>();
@Override
public void handle(HttpExchange exchange) throws IOException {
- String path = HttpServerUtils.subPath(exchange);
ContentSession session = HttpServerUtils.getContentSession(contentRepository, exchange);
- // Content content = session.get(path);
- // PARAMETERS
+ String path = HttpServerUtils.subPath(exchange);
+
+ // content path
+ final String pathToUse = path;
+ String fileName = null;
+ boolean zipped = false;
+// int lastSlash = path.lastIndexOf('/');
+// if (lastSlash > 0) {
+// fileName = path.substring(lastSlash + 1);
+// }
+// if (fileName != null) {
+// pathToUse = path.substring(0, lastSlash);
+// if (path.endsWith(".zip")) {
+// zipped = true;
+// }
+// } else {
+// pathToUse = path;
+// }
+
Map<String, List<String>> parameters = HttpServerUtils.parseParameters(exchange);
- String cql = getKvpParameter(parameters, CQL_FILTER);
- String typeNamesStr = getKvpParameter(parameters, TYPE_NAMES);
- String outputFormat = getKvpParameter(parameters, OUTPUT_FORMAT);
+
+ // PARAMETERS
+ String cql = getKvpParameter(parameters, WfsKvp.CQL_FILTER);
+ String typeNamesStr = getKvpParameter(parameters, WfsKvp.TYPE_NAMES);
+ String outputFormat = getKvpParameter(parameters, WfsKvp.OUTPUT_FORMAT);
if (outputFormat == null) {
outputFormat = "application/json";
}
- String bboxStr = getKvpParameter(parameters, BBOX);
- log.debug(bboxStr);
+
+ // TODO deal with multiple
+ String formatOption = getKvpParameter(parameters, WfsKvp.FORMAT_OPTIONS);
+ if (formatOption != null) {
+ if (formatOption.startsWith(WfsKvp.FILENAME_))
+ fileName = formatOption.substring(WfsKvp.FILENAME_.length());
+ }
+ if (fileName != null && fileName.endsWith(".zip"))
+ zipped = true;
+
+ // bbox
+ String bboxStr = getKvpParameter(parameters, WfsKvp.BBOX);
+ if (log.isTraceEnabled())
+ log.trace(bboxStr);
final Envelope bbox;
if (bboxStr != null) {
String srs;
bbox = null;
}
- switch (outputFormat) {
- case "application/json" -> {
- exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/json");
+ // response headers
+ exchange.getResponseHeaders().set(HttpHeader.DATE.getHeaderName(), Long.toString(System.currentTimeMillis()));
+
+ if (fileName != null) {
+ exchange.getResponseHeaders().set(HttpHeader.CONTENT_DISPOSITION.getHeaderName(),
+ HttpHeader.ATTACHMENT + ";" + HttpHeader.FILENAME + "=\"" + fileName + "\"");
+
}
- case "GML3" -> {
+
+ // content type
+ if (zipped) {
+ exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/zip");
+
+ } else {
+ switch (outputFormat) {
+ case "application/json" -> {
+ exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/json");
+ }
+ case "GML3" -> {
// exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/gml+xml");
- exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/xml");
- }
+ exchange.getResponseHeaders().set(HttpHeader.CONTENT_TYPE.getHeaderName(), "application/xml");
+ }
- default -> throw new IllegalArgumentException("Unexpected value: " + outputFormat);
+ default -> throw new IllegalArgumentException("Unexpected value: " + outputFormat);
+ }
}
List<QName> typeNames = new ArrayList<>();
// QUERY
Stream<Content> res = session.search((search) -> {
if (cql != null) {
- CqlUtils.filter(search.from(path), cql);
+ CqlUtils.filter(search.from(pathToUse), cql);
} else {
- search.from(path);
+ search.from(pathToUse);
}
for (QName typeName : typeNames) {
FeatureAdapter featureAdapter = featureAdapters.get(typeName);
if (featureAdapter == null)
throw new IllegalStateException("No feature adapter found for " + typeName);
// f.isContentClass(typeName);
- featureAdapter.addConstraintsForFeature((AndFilter) search.getWhere(), typeName);
+ RemoteAuthUtils.doAs(() -> {
+ featureAdapter.addConstraintsForFeature((AndFilter) search.getWhere(), typeName);
+ return null;
+ }, new RemoteAuthHttpExchange(exchange));
}
if (bbox != null) {
exchange.sendResponseHeaders(200, 0);
final int BUFFER_SIZE = 100 * 1024;
- try (BufferedOutputStream out = new BufferedOutputStream(exchange.getResponseBody(), BUFFER_SIZE)) {
+ try (OutputStream out = zipped ? new ZipOutputStream(exchange.getResponseBody())
+ : new BufferedOutputStream(exchange.getResponseBody(), BUFFER_SIZE)) {
+ if (out instanceof ZipOutputStream zipOut) {
+ String unzippedFileName = fileName.substring(0, fileName.length() - ".zip".length());
+ zipOut.putNextEntry(new ZipEntry(unzippedFileName));
+ }
+
if ("GML3".equals(outputFormat)) {
encodeCollectionAsGML(res, out);
} else if ("application/json".equals(outputFormat)) {
*
* @see https://docs.ogc.org/is/09-025r2/09-025r2.html#19
*/
- protected String getKvpParameter(Map<String, List<String>> parameters, String key) {
+ protected String getKvpParameter(Map<String, List<String>> parameters, WfsKvp key) {
Objects.requireNonNull(key, "KVP key cannot be null");
// let's first try the default (CAML case) which should be more efficient
- List<String> values = parameters.get(key);
+ List<String> values = parameters.get(key.getKey());
if (values == null) {
// then let's do an ignore case comparison of the key
keys: for (String k : parameters.keySet()) {
- if (key.equalsIgnoreCase(k)) {
+ if (key.getKey().equalsIgnoreCase(k)) {
values = parameters.get(k);
break keys;
}
protected Geometry getDefaultGeometry(Content content) {
if (content.hasContentClass(EntityType.geopoint)) {
- double latitude = content.get(WGS84PosName.lat, Double.class).get();
- double longitude = content.get(WGS84PosName.lon, Double.class).get();
-
- Coordinate coordinate = new Coordinate(longitude, latitude);
- Point the_geom = JTS.GEOMETRY_FACTORY.createPoint(coordinate);
- return the_geom;
+ return GeoEntityUtils.toPoint(content);
}
return null;
}
*/
public void addFeatureAdapter(FeatureAdapter featureAdapter, Map<String, Object> properties) {
- List<String> typeNames = LangUtils.toStringList(properties.get(TYPE_NAMES));
+ List<String> typeNames = LangUtils.toStringList(properties.get(WfsKvp.TYPE_NAMES.getKey()));
if (typeNames.isEmpty()) {
log.warn("FeatureAdapter " + featureAdapter.getClass() + " does not declare type names. Ignoring it...");
return;
}
public void removeFeatureAdapter(FeatureAdapter featureAdapter, Map<String, Object> properties) {
- List<String> typeNames = LangUtils.toStringList(properties.get(TYPE_NAMES));
+ List<String> typeNames = LangUtils.toStringList(properties.get(WfsKvp.TYPE_NAMES.getKey()));
if (!typeNames.isEmpty()) {
// ignore if noe type name declared
return;