diff --git a/adapters/klab.adapter.datacube/pom.xml b/adapters/klab.adapter.datacube/pom.xml
index e02c10f85c..1f460d015e 100644
--- a/adapters/klab.adapter.datacube/pom.xml
+++ b/adapters/klab.adapter.datacube/pom.xml
@@ -1,6 +1,6 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
org.integratedmodelling
diff --git a/adapters/klab.adapter.datacube/src/main/java/org/integratedmodelling/adapter/datacube/ChunkedDatacubeRepository.java b/adapters/klab.adapter.datacube/src/main/java/org/integratedmodelling/adapter/datacube/ChunkedDatacubeRepository.java
index 835ede208d..7e0a232ea6 100644
--- a/adapters/klab.adapter.datacube/src/main/java/org/integratedmodelling/adapter/datacube/ChunkedDatacubeRepository.java
+++ b/adapters/klab.adapter.datacube/src/main/java/org/integratedmodelling/adapter/datacube/ChunkedDatacubeRepository.java
@@ -24,10 +24,10 @@
import java.util.logging.Level;
import javax.annotation.Nullable;
-import javax.media.jai.iterator.RandomIter;
-import javax.media.jai.iterator.RandomIterFactory;
import org.apache.commons.io.FileUtils;
+import org.eclipse.imagen.iterator.RandomIter;
+import org.eclipse.imagen.iterator.RandomIterFactory;
import org.geotools.coverage.grid.GridCoverage2D;
import org.integratedmodelling.adapter.datacube.api.IDatacube;
import org.integratedmodelling.klab.Configuration;
@@ -40,8 +40,6 @@
import org.integratedmodelling.klab.api.data.IResource.Availability;
import org.integratedmodelling.klab.api.data.adapters.IKlabData.Builder;
import org.integratedmodelling.klab.api.data.mediation.IUnit;
-import org.integratedmodelling.klab.api.observations.IState;
-import org.integratedmodelling.klab.api.observations.scale.IExtent;
import org.integratedmodelling.klab.api.observations.scale.IScale;
import org.integratedmodelling.klab.api.observations.scale.space.IGrid;
import org.integratedmodelling.klab.api.observations.scale.space.ISpace;
diff --git a/adapters/klab.ogc/pom.xml b/adapters/klab.ogc/pom.xml
index 08cd69ca5e..1c04bee848 100644
--- a/adapters/klab.ogc/pom.xml
+++ b/adapters/klab.ogc/pom.xml
@@ -47,6 +47,22 @@
1.68
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
org.hortonmachine
hm-gears
@@ -56,10 +72,15 @@
javax.measure
jsr-275
+
org.mongodb
mongo-java-driver
+
+ edu.colostate.omslab
+ oms
+
@@ -68,7 +89,6 @@
aws-lightweight-client-java
${s3.version}
-
Filter later :)
- if (resourceTime != null && resourceTime.getStart() != null && resourceTime.getEnd() != null && resourceTime.getCoveredExtent() > 0) {
- time = validateTemporalDimension(time, resourceTime);
- }
- ITimeInstant start = time.getStart();
- ITimeInstant end = time.getEnd();
- collection.setTimestampFilter(new Date(start.getMilliseconds()), new Date(end.getMilliseconds()));
+ GridCoverage2D coverage = null;
+
+ collection.setBboxFilter(new double[] {
+ bbox.get(0),
+ bbox.get(2),
+ bbox.get(1),
+ bbox.get(3)
+ });
- GridCoverage2D coverage = null;
- try {
+ // Allow transform ensures the process to finish, but I would not bet on the resulting
+ // data
+ if (assetPredicate == null) {
+ // NO JSONSelector and JSONValue found, NO assetID was passed as well
+ scope.getMonitor().debug("Query STAC " + collectionUrl + "to get the features");
+ // Only get the features from STAC Collection, no need to interact with Rasters
+ FeatureSource source;
+ try {
+ source = STACFeatureExtension.getFeatures(catalogData, collectionId, bbox, effectiveTime.getStart(),
+ effectiveTime.getEnd());
+ } catch (Exception e) {
+ manager.close();
+ throw new KlabResourceAccessException("Cannot extract features from STAC Collection - " + e.getMessage());
+ }
+ encoder = new VectorEncoder();
+ ((VectorEncoder) encoder).encodeFromFeatures(source, resource, urnParameters, geometry, builder, scope);
+ manager.close();
+ return;
+ }
+
List items = collection.searchItems();
-
if (items.isEmpty()) {
manager.close();
- throw new KlabIllegalStateException("No STAC items found for this context.");
+ throw new KlabIllegalStateException("No STAC items found for this context, check the Spatial/ Temporal bounds of items and the Context");
}
- scope.getMonitor().debug("Found " + items.size() + " STAC items.");
if (mergeMode == HMRaster.MergeMode.SUBSTITUTE) {
sortByDate(items, scope.getMonitor());
@@ -374,62 +482,178 @@ public void getEncodedData(IResource resource, Map urnParameters
IGrid grid = space.getGrid();
RegionMap region = RegionMap.fromBoundsAndGrid(space.getEnvelope().getMinX(), space.getEnvelope().getMaxX(),
- space.getEnvelope().getMinY(), space.getEnvelope().getMaxY(), (int) grid.getXCells(),
- (int) grid.getYCells());
+ space.getEnvelope().getMinY(), space.getEnvelope().getMaxY(), (int) grid.getXCells(), (int) grid.getYCells());
ReferencedEnvelope regionEnvelope = new ReferencedEnvelope(region.toEnvelope(),
space.getProjection().getCoordinateReferenceSystem());
RegionMap regionTransformed = RegionMap.fromEnvelopeAndGrid(regionEnvelope, (int) grid.getXCells(),
(int) grid.getYCells());
- Set EPSGsAtItems = items.stream().map(i -> i.getEpsg()).collect(Collectors.toUnmodifiableSet());
- if (EPSGsAtItems.size() > 1) {
- scope.getMonitor().warn("Multiple EPSGs found on the items " + EPSGsAtItems.toString() + ". The transformation process could affect the data.");
- }
- if (resource.getParameters().contains("awsRegion")) {
- String bucketRegion = resource.getParameters().get("awsRegion", String.class);
- Client s3Client = buildS3Client(bucketRegion);
+ if (resource.getParameters().contains("s3EndpointUrl")) {
+ String s3EndpointURL = resource.getParameters().get("s3EndpointUrl", String.class);
+ Client s3Client = buildS3Client(s3EndpointURL);
collection.setS3Client(s3Client);
}
+ var time = effectiveTime;
+ // Filter here based on time, since in some STAC collections they don't yet support
+ // temporal filtering :( like ECDC
+ items = items.stream()
+ .filter(item -> isWithinRange(item, time.getStart().getMilliseconds(), time.getEnd().getMilliseconds()))
+ .collect(Collectors.toList());
+
+ if (items.size() == 0) {
+ manager.close();
+ throw new KlabIllegalStateException(
+ "No STAC items found covering the entire time duration of the context requested");
+ } else {
+ scope.getMonitor().debug("Found " + items.size() + " STAC items satisfying the temporal constraint.");
+ }
- // Allow transform ensures the process to finish, but I would not bet on the resulting
- // data.
- final boolean allowTransform = true;
- HMRaster outRaster = collection.readRasterBandOnRegion(regionTransformed, assetId, items, allowTransform, mergeMode, lpm);
- coverage = outRaster.buildCoverage();
+ // Once the support for customized predicate is added, we can apply for features as well
+
+ var pred = assetPredicate;
+ Set EPSGAtAssets = items.stream()
+ .flatMap(item -> item.getAssets().stream().filter(pred).findFirst()
+ .map(asset -> asset.getEpsg() != null ? asset.getEpsg() : item.getEpsg()).stream())
+ .collect(Collectors.toUnmodifiableSet());
+
+ if (EPSGAtAssets.size() > 1) {
+ scope.getMonitor().warn("Multiple EPSGs found on the assets in items " + EPSGAtAssets.toString() + "."
+ + "The transformation process could affect the data.");
+ }
+
+
+ // Specific Implementation for the Slow Requests flow in WEED
+ if (collection.getId().contains("EU_modelV2-1-MECE")
+ && resource.getUrn().contains("im.resources-main")) {
+ Geometry unionMLStacInference = UnaryUnionOp.union(
+ items.stream()
+ .map(item -> item.getGeometry())
+ .filter(g -> g != null && !g.isEmpty())
+ .collect(Collectors.toList())
+ );
+
+ if (!unionMLStacInference.contains(poly)) {
+ scope.getMonitor().warn("The requested extend for ML inferences is not completely contained in STAC, Starting ML Inference Request");
+
+ OpenEO service = OpenEOAdapter.getClient("openeo_weed.dataspace.copernicus.eu");
+ List processes = new ArrayList<>();
+ String processNamespace = "https://raw.githubusercontent.com/ESA-WEED-project/OpenEO-UDP-UDF-catalogue/refs/heads/main/UDP/json/udp_starter.json";
+ String processID = "udp_starter";
+
+ Process process = JsonUtils.load(new URL(processNamespace),
+ Process.class);
+ process.encodeSelf(processNamespace);
+ processes.add(process);
+
+ JSONObject arguments = new JSONObject()
+ .put("bbox", new JSONObject()
+ .put("crs", 4326)
+ .put("west", bbox.get(0))
+ .put("south", bbox.get(3))
+ .put("east", bbox.get(1))
+ .put("north", bbox.get(2)))
+ .put("digitalId", "AM1729") // Forms the STAC coordinate later
+ .put("scenarioId", "DT_SLOW_FLOW") // Forms the STAC coordinate later
+ .put("year", ctxTime.getEnd().getYear())
+ .put("onnx_model", "EUNIS2021plus_panEU_v201_2024_OneZone") // Hardcoding for now only for Europe, until the "BEST" model is decided!
+ .put("dt_url", "https://services.integratedmodelling.org/runtime/main/api/v1/dt/ESA_INSTITUTIONAL.3vh554o6h6c");
+
+
+ OpenEOFuture job = service.submit(processID, arguments,
+ scope.getMonitor(), processes.toArray(new Process[processes.size()]));
+
+ if (job.isCancelled()) {
+ scope.getMonitor().warn("job canceled");
+ } else if (job.getError() != null) {
+ scope.getMonitor().error(job.getError());
+ } else {
+ scope.getMonitor().info("Inference Request has been submitted to the ML Workflows");
+ }
+ }
+ }
+
+ HMRaster outRaster = collection.readRasterBandOnRegion(regionTransformed, assetPredicate, items, allowTransform,
+ MergeMode.SUBSTITUTE, lpm);
+ if (outRaster == null) {
+ scope.getMonitor().error("Unable to build the output from the STAC Resource");
+ throw new KlabIllegalStateException("Unable to build the output from the STAC Resource");
+ }
+ CoordinateReferenceSystem targetCRS = HMCrsRegistry.INSTANCE.getCrs("4326");
+ if (!HMCrsRegistry.crsEquals(outRaster.getCrs(),targetCRS)) {
+ var transformer = new HMCrsTransformer(outRaster.getCrs(), targetCRS);
+ transformer.setAcceptLenientDatumShift(true);
+ outRaster = transformer.transform(outRaster);
+ }
+
+
+ HMRaster paddedRaster = new HMRasterWritableBuilder().setName("padded").setRegion(region)
+ .setCrs(targetCRS).setNoValue(outRaster.getNovalue()).build();
+ paddedRaster.mapRaster(null, outRaster, null);
+ coverage = paddedRaster.buildCoverage();
+
+ if (bandIndex != null) { // Which means theat it's a Multi Band COG
+ coverage = (GridCoverage2D) Operations.DEFAULT.selectSampleDimension(coverage, new int[]{bandIndex});
+ }
+
manager.close();
+ encoder = new RasterEncoder();
+ ((RasterEncoder) encoder).encodeFromCoverage(resource, urnParameters, coverage, geometry, builder, scope);
} catch (Exception e) {
+ e.printStackTrace();
throw new KlabInternalErrorException("Cannot build STAC raster output. Reason " + e.getMessage());
}
- encoder = new RasterEncoder();
- ((RasterEncoder)encoder).encodeFromCoverage(resource, urnParameters, coverage, geometry, builder, scope);
}
- private boolean isFeatureInTimeRange(Time time2, SimpleFeature f) {
- Date datetime = (Date) f.getAttribute("datetime");
- if (datetime != null) {
- if (isDateWithinRange(time2, datetime)) {
- return true;
+ private Predicate getAssetPredicateFromJSONSelector(IResource resource) {
+ String jsonSelector = resource.getParameters().get("jsonSelector", String.class);
+ String jsonValue = resource.getParameters().get("jsonValue", String.class);
+ if (jsonSelector != null && !jsonSelector.isBlank() && jsonValue != null) {
+ try {
+ return STACPathExpression.STACAssetPredicate.fromHMStacAsset(jsonSelector, jsonValue);
+ } catch (IllegalArgumentException e) {
+ throw new KlabIllegalArgumentException("Invalid STAC asset JSON selector: " + jsonSelector);
}
+ } else {
+ throw new KlabIllegalArgumentException("Either asset or both jsonSelector and jsonValue must be provided");
}
+ }
- Date itemStart = (Date) f.getAttribute("start_datetime");
- if (itemStart == null) {
- return false;
- }
- Date itemEnd = (Date) f.getAttribute("end_datetime");
- if (itemEnd == null) {
- return itemStart.toInstant().getEpochSecond() <= time2.getStart().getMilliseconds();
+ /*
+ To check if an Item (of type HMStacItem) is within a time range
+ */
+ private boolean isWithinRange(HMStacItem item, long startMillis, long endMillis) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ String startTimestamp = item.getStartTimestamp();
+ String endTimestamp = item.getEndTimestamp();
+
+ if (startTimestamp == null || endTimestamp == null) {
+ return true; // Assume the time part is ok
}
- if (isDateWithinRange(time2, itemStart) || isDateWithinRange(time2, itemEnd)) {
- return true;
+
+ try {
+
+ long itemStart = LocalDateTime.parse(item.getStartTimestamp(), formatter).atZone(ZoneOffset.UTC).toInstant()
+ .toEpochMilli();
+
+ long itemEnd = LocalDateTime.parse(item.getEndTimestamp(), formatter).atZone(ZoneOffset.UTC).toInstant()
+ .toEpochMilli();
+
+ return (startMillis >= itemStart || Math.abs(startMillis - itemStart) < 10000)
+ && (endMillis <= itemEnd || Math.abs(endMillis - itemEnd) < 10000); // Allow some small tolerance
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
}
- return false;
}
- private List getFeaturesFromStaticCollection(String collectionUrl, JSONObject collectionData, String collectionId) {
- List links = collectionData.getJSONArray("links").toList().stream().filter(link -> ((JSONObject)link).getString("rel").equalsIgnoreCase("item")).toList();
- List urlOfLinks = links.stream().map(link -> STACUtils.getUrlOfItem(collectionUrl, collectionId, link.getString("href"))).toList();
+ private List getFeaturesFromStaticCollection(String collectionUrl, JSONObject collectionData,
+ String collectionId) {
+ List links = collectionData.getJSONArray("links").toList().stream()
+ .filter(link -> ((JSONObject) link).getString("rel").equalsIgnoreCase("item")).toList();
+ List urlOfLinks = links.stream()
+ .map(link -> STACUtils.getUrlOfItem(collectionUrl, collectionId, link.getString("href"))).toList();
return urlOfLinks.stream().map(i -> {
try {
return STACUtils.getItemAsFeature(i);
diff --git a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACImporter.java b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACImporter.java
index dc768f5093..1d640dd5a3 100644
--- a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACImporter.java
+++ b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACImporter.java
@@ -9,10 +9,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import org.integratedmodelling.kim.api.IParameters;
-import org.integratedmodelling.klab.Logging;
import org.integratedmodelling.klab.Resources;
import org.integratedmodelling.klab.api.data.ILocator;
import org.integratedmodelling.klab.api.data.IResource;
@@ -44,22 +42,20 @@ public boolean acceptsMultiple() {
return false;
}
+ private boolean hasAssetSelector(IParameters parameters) {
+ return (parameters.contains("asset") || (parameters.contains("jsonSelector") && parameters.contains("jsonValue")));
+ }
+
private void importCollection(List ret, IParameters parameters, IProject project, IMonitor monitor)
- throws MalformedURLException {
+ throws Exception {
String collectionUrl = parameters.get("collection", String.class);
JSONObject collectionData = STACUtils.requestMetadata(collectionUrl, "collection");
String collectionId = STACCollectionParser.readCollectionId(collectionData);
parameters.put("collectionId", collectionId);
- String regex = null;
- if (parameters.contains("regex")) {
- regex = parameters.get(Resources.REGEX_ENTRY, String.class);
- parameters.remove(Resources.REGEX_ENTRY);
- }
-
boolean isBulkImport = parameters.contains("bulkImport");
parameters.remove("bulkImport");
- if (!parameters.contains("asset") && !isBulkImport) {
+ if (!hasAssetSelector(parameters) && !isBulkImport) {
Builder builder = buildResource(parameters, project, monitor, collectionId);
if (builder != null) {
ret.add(builder);
@@ -68,19 +64,86 @@ private void importCollection(List ret, IParameters parameters,
}
return;
}
- JSONObject assets = STACCollectionParser.readAssetsFromCollection(collectionUrl, collectionData);
+
+ String assetId = parameters.get("asset", String.class);
+ String resourceUrn = collectionId;
+ JSONObject assetData = null;
+ /*
+ This is only for the part, to check the Asset is under an S3 Bucket
+ */
+ if (assetId != null) {
+ JSONObject assetNode = STACCollectionParser.readAssetInformationFromCollection(collectionUrl, collectionData, assetId);
+ //assetData = assetNode.getJSONObject(assetId);
+ assetData = (JSONObject) assetNode.toMap()
+ .values()
+ .iterator()
+ .next();
+ /*
+ * If the particular asset Id wasn't found, then
+ * still proceed to create the resource since it can happen
+ * for a number of reasons. Pagination, STAC Backend etc.
+ * In that case however, a better handling of S3 URL needs to figured out
+ */
+ if (assetData != null) {
+ if (!STACAssetParser.isSupportedMediaType(assetData)) {
+ throw new Exception("Unsupported media type for the asset");
+ }
+ String href = assetData.getString("href");
+ if (S3URLUtils.isS3Endpoint(href)) {
+ String[] bucketAndObject = href.split("://")[1].split("/", 2);
+ String s3Region = "unknown"; // TODO resolve the region
+ parameters.put("awsRegion", s3Region);
+ }
+ }
+ resourceUrn = collectionId + "-" + assetId;
+ }
+
+ //TODO: Check if assetId is null, and instead a jsonSelector and Value is passed!
+ //else if (parameters.get("jsonSelector", String.class) != null) {
+ // String selector = parameters.get("jsonSelector", String.class);
+ // String val = parameters.get("jsonValue", String.class);
+ // assetData = STACCollectionParser.readAssetInformationFromCollection(collectionUrl, collectionData, null, STACPathExpression.STACAssetPredicate.fromKongJsonObject(selector, val));
+ //}
+
+ String href = assetData.getString("href");
+ if (S3URLUtils.isS3Endpoint(href)) {
+ String[] bucketAndObject = href.split("://")[1].split("/", 2);
+ String s3Region = "unknown"; // TODO resolve the region
+ parameters.put("awsRegion", s3Region);
+ }
+
+ Builder builder = buildResource(parameters, project, monitor, resourceUrn);
+ if (builder != null) {
+ ret.add(builder);
+ } else {
+ monitor.warn("STAC resource with asset " + resourceUrn + " is invalid and cannot be imported");
+ }
+
+ // Think of a way to do the REGEX matching. Possibly not worth it to support
+ // this for something as diverse as STAC
+
+ // String regex = null;
+ if (parameters.contains("regex")) {
+ // regex = parameters.get(Resources.REGEX_ENTRY, String.class);
+ parameters.remove(Resources.REGEX_ENTRY);
+ }
+
+ /*
Set assetIds = STACAssetMapParser.readAssetNames(assets);
for(String assetId : assetIds) {
if (regex != null && !assetId.matches(regex)) {
Logging.INSTANCE.info("Asset " + assetId + " doesn't match REGEX, skipped");
continue;
}
-
+
JSONObject assetData = STACAssetMapParser.getAsset(assets, assetId);
- if (!STACAssetParser.isSupportedMediaType(assetData)) {
- Logging.INSTANCE.info("Asset " + assetId + " doesn't have a supported media type, skipped");
- continue;
+ if (assetData != null) {
+ if (!STACAssetParser.isSupportedMediaType(assetData)) {
+ Logging.INSTANCE.info("Asset " + assetId + " doesn't have a supported media type, skipped");
+ continue;
+ }
}
+
parameters.put("asset", assetId);
String resourceUrn = collectionId + "-" + assetId;
String href = assetData.getString("href");
@@ -89,7 +152,7 @@ private void importCollection(List ret, IParameters parameters,
String s3Region = "unknown"; // TODO resolve the region
parameters.put("awsRegion", s3Region);
}
-
+
Builder builder = buildResource(parameters, project, monitor, resourceUrn);
if (builder != null) {
ret.add(builder);
@@ -97,12 +160,14 @@ private void importCollection(List ret, IParameters parameters,
monitor.warn("STAC resource with asset " + resourceUrn + " is invalid and cannot be imported");
}
}
+
+ */
}
- private Builder buildResource(IParameters parameters, IProject project, IMonitor monitor, String resourceUrn) throws MalformedURLException {
- Builder builder = validator.validate(
- Resources.INSTANCE.createLocalResourceUrn(resourceUrn, project), new URL(parameters.get("collection", String.class)),
- parameters, monitor);
+ private Builder buildResource(IParameters parameters, IProject project, IMonitor monitor, String resourceUrn)
+ throws MalformedURLException {
+ Builder builder = validator.validate(Resources.INSTANCE.createLocalResourceUrn(resourceUrn, project),
+ new URL(parameters.get("collection", String.class)), parameters, monitor);
if (builder == null) {
return null;
diff --git a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACPathExpression.java b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACPathExpression.java
new file mode 100644
index 0000000000..bc4f73bd67
--- /dev/null
+++ b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACPathExpression.java
@@ -0,0 +1,414 @@
+package org.integratedmodelling.klab.stac;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.TextNode;
+
+import kong.unirest.json.JSONObject;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import org.hortonmachine.gears.io.stac.HMStacAsset;
+
+public final class STACPathExpression {
+
+ private final List path;
+
+ public static final String PREDICATE_EO_BANDS_NAME = "eo:bands.name";
+ public static final String MEDIA_TYPE_BANDS_NAME = "type";
+
+ private STACPathExpression(List path) {
+ this.path = path;
+ }
+
+ public static STACPathExpression parse(String jsonPath) {
+ if (jsonPath == null || jsonPath.isBlank()) {
+ throw new IllegalArgumentException("JSON path cannot be empty");
+ }
+ return new STACPathExpression(parsePath(jsonPath));
+ }
+
+ public boolean matches(JsonNode root, String expectedValue) {
+ List resolvedNodes = resolve(root);
+
+ for(JsonNode node : resolvedNodes) {
+ if (jsonValueEquals(node, expectedValue)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public List resolve(JsonNode root) {
+ List currentNodes = new ArrayList<>();
+ currentNodes.add(root);
+
+ for(PathPart part : path) {
+ List nextNodes = new ArrayList<>();
+
+ for(JsonNode current : currentNodes) {
+ if (current == null || current.isNull() || current.isMissingNode()) {
+ continue;
+ }
+
+ resolvePart(current, part, nextNodes);
+ }
+
+ currentNodes = nextNodes;
+
+ if (currentNodes.isEmpty()) {
+ break;
+ }
+ }
+
+ return currentNodes;
+ }
+
+ private static void resolvePart(JsonNode current, PathPart part, List nextNodes) {
+ if (current.isArray()) {
+ resolveFromArray(current, part, nextNodes);
+ } else if (current.isObject()) {
+ resolveFromObject(current, part, nextNodes);
+ }
+ }
+
+ private static void resolveFromArray(JsonNode arrayNode, PathPart part, List nextNodes) {
+ if (part.arrayMode() == ArrayMode.INDEX) {
+ JsonNode indexedNode = arrayNode.get(part.arrayIndex());
+
+ if (isUsable(indexedNode)) {
+ resolvePart(indexedNode, new PathPart(part.fieldName(), ArrayMode.NONE, null), nextNodes);
+ }
+
+ } else {
+ for(JsonNode element : arrayNode) {
+ if (isUsable(element)) {
+ resolvePart(element, part, nextNodes);
+ }
+ }
+ }
+ }
+
+ private static void resolveFromObject(JsonNode objectNode, PathPart part, List nextNodes) {
+ JsonNode directField = objectNode.get(part.fieldName());
+
+ if (isUsable(directField)) {
+ addResolvedField(directField, part, nextNodes);
+ } else {
+ objectNode.fields().forEachRemaining(entry -> {
+ String mapKey = entry.getKey();
+ JsonNode mapValue = entry.getValue();
+
+ if (!isUsable(mapValue) || !mapValue.isObject()) {
+ return;
+ }
+
+ if ("id".equalsIgnoreCase(part.fieldName())) {
+ nextNodes.add(TextNode.valueOf(mapKey));
+ return;
+ }
+
+ JsonNode nestedField = mapValue.get(part.fieldName());
+
+ if (isUsable(nestedField)) {
+ addResolvedField(nestedField, part, nextNodes);
+ }
+ });
+
+ }
+ }
+
+ private static void addResolvedField(JsonNode fieldNode, PathPart part, List nextNodes) {
+ if (part.arrayMode() == ArrayMode.INDEX) {
+ if (fieldNode.isArray()) {
+ JsonNode indexedNode = fieldNode.get(part.arrayIndex());
+
+ if (isUsable(indexedNode)) {
+ nextNodes.add(indexedNode);
+ }
+ }
+ } else if (fieldNode.isArray()) {
+ for(JsonNode element : fieldNode) {
+ if (isUsable(element)) {
+ nextNodes.add(element);
+ }
+ }
+ } else {
+ nextNodes.add(fieldNode);
+ }
+ }
+
+ private static boolean isUsable(JsonNode node) {
+ return node != null && !node.isNull() && !node.isMissingNode();
+ }
+
+ private static List parsePath(String jsonPath) {
+ String[] tokens = jsonPath.split("\\.");
+
+ List parts = new ArrayList<>();
+
+ for(String token : tokens) {
+ String trimmed = token.trim();
+
+ if (trimmed.isEmpty()) {
+ throw new IllegalArgumentException("Invalid empty path element");
+ }
+
+ parts.add(PathPart.parse(trimmed));
+ }
+
+ return parts;
+ }
+
+ private static boolean valueEquals(Object actualValue, String expectedValue) {
+ if (actualValue == null) {
+ return expectedValue == null;
+ }
+
+ if (expectedValue == null) {
+ return false;
+ }
+
+ if (actualValue instanceof Number number) {
+ return numberEquals(number, expectedValue);
+ }
+
+ if (actualValue instanceof Boolean bool) {
+ return Boolean.toString(bool).equalsIgnoreCase(expectedValue);
+ }
+
+ return Objects.equals(String.valueOf(actualValue), expectedValue);
+ }
+
+ private static boolean jsonValueEquals(JsonNode actualValue, String expectedValue) {
+ if (actualValue == null || actualValue.isNull() || actualValue.isMissingNode()) {
+ return expectedValue == null;
+ }
+
+ if (expectedValue == null || !actualValue.isValueNode()) {
+ return false;
+ }
+
+ if (actualValue.isNumber()) {
+ return numberEquals(actualValue.decimalValue(), expectedValue);
+ }
+
+ if (actualValue.isBoolean()) {
+ return Boolean.toString(actualValue.booleanValue()).equalsIgnoreCase(expectedValue);
+ }
+
+ if (actualValue.isTextual()) {
+ return Objects.equals(actualValue.asText(), expectedValue);
+ }
+
+ return false;
+ }
+
+ private static boolean numberEquals(Number actualValue, String expectedValue) {
+ try {
+ BigDecimal actual = new BigDecimal(actualValue.toString());
+ BigDecimal expected = new BigDecimal(expectedValue);
+
+ return actual.compareTo(expected) == 0;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ public record PathPart(String fieldName, ArrayMode arrayMode, Integer arrayIndex) {
+
+ public static PathPart parse(String token) {
+ int bracketStart = token.indexOf('[');
+
+ if (bracketStart < 0) {
+ return new PathPart(token, ArrayMode.NONE, null);
+ }
+
+ int bracketEnd = token.indexOf(']', bracketStart);
+
+ if (bracketEnd < 0) {
+ throw new IllegalArgumentException("Invalid array syntax in path element: " + token);
+ }
+
+ if (bracketEnd != token.length() - 1) {
+ throw new IllegalArgumentException("Unexpected characters after array syntax in path element: " + token);
+ }
+
+ String fieldName = token.substring(0, bracketStart).trim();
+ String indexText = token.substring(bracketStart + 1, bracketEnd).trim();
+
+ if (fieldName.isEmpty()) {
+ throw new IllegalArgumentException("Field name cannot be empty in path element: " + token);
+ }
+
+ int index;
+ try {
+ index = Integer.parseInt(indexText);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid array index in path element: " + token, e);
+ }
+ if (index < 0) {
+ throw new IllegalArgumentException("Array index cannot be negative in path element: " + token);
+ }
+ return new PathPart(fieldName, ArrayMode.INDEX, index);
+ }
+ }
+
+ public enum ArrayMode {
+ NONE, INDEX
+ }
+
+ public static final class STACAssetPredicate {
+
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ private STACAssetPredicate() {
+ }
+
+ public static Predicate fromJsonPath(String jsonPath, String expectedValue,
+ Function jsonNodeExtractor) {
+ STACPathExpression expression = STACPathExpression.parse(jsonPath);
+
+ return object -> {
+ if (object == null) {
+ return false;
+ }
+
+ JsonNode node = jsonNodeExtractor.apply(object);
+
+ if (node == null || node.isNull() || node.isMissingNode()) {
+ return false;
+ }
+
+ /*
+ * Check if {"a.b": expectedValue} exists and also {"a": { "b": expectedValue }}
+ */
+
+ JsonNode directValue = node.get(jsonPath);
+ if (directValue != null && !directValue.isNull()) {
+ return expectedValue.equals(directValue.asText());
+ }
+
+ return expression.matches(node, expectedValue);
+ };
+ }
+
+ public static Predicate fromHMStacAsset(String jsonPath, String expectedValue) {
+ return fromJsonPath(jsonPath, expectedValue, asset -> asset == null ? null : asset.getAssetNode());
+ }
+
+ public static Predicate fromJsonNode(String jsonPath, String expectedValue) {
+ return fromJsonPath(jsonPath, expectedValue, node -> node);
+ }
+
+ public static Predicate fromKongJsonObject(String jsonPath, String expectedValue) {
+ return fromJsonPath(jsonPath, expectedValue, STACAssetPredicate::toJsonNode);
+ }
+
+ private static JsonNode toJsonNode(JSONObject jsonObject) {
+ if (jsonObject == null) {
+ return null;
+ }
+
+ try {
+ return OBJECT_MAPPER.readTree(jsonObject.toString());
+ } catch (JsonProcessingException e) {
+ throw new IllegalArgumentException("Cannot convert JSONObject to JsonNode", e);
+ }
+ }
+
+ public static Predicate fromHMStacAssetAttribute(String attributeName, String expectedValue) {
+ AssetAttribute attribute = AssetAttribute.fromName(attributeName);
+
+ return asset -> {
+ if (asset == null) {
+ return false;
+ }
+
+ Object actualValue = attribute.read(asset);
+
+ return valueEquals(actualValue, expectedValue);
+ };
+ }
+
+ public static Predicate fromHMStacAssetId(String expectedValue) {
+ return fromHMStacAssetAttribute("id", expectedValue);
+ }
+ }
+
+ public enum AssetAttribute {
+
+ ID("id") {
+ @Override
+ Object read(HMStacAsset asset) {
+ return asset.getId();
+ }
+ },
+
+ TITLE("title") {
+ @Override
+ Object read(HMStacAsset asset) {
+ return asset.getTitle();
+ }
+ },
+
+ TYPE("type") {
+ @Override
+ Object read(HMStacAsset asset) {
+ return asset.getType();
+ }
+ },
+
+ VALID("valid") {
+ @Override
+ Object read(HMStacAsset asset) {
+ return asset.isValid();
+ }
+ },
+
+ EPSG("epsg") {
+ @Override
+ Object read(HMStacAsset asset) {
+ return asset.getEpsg();
+ }
+ },
+
+ NON_VALID_REASON("nonValidReason") {
+ @Override
+ Object read(HMStacAsset asset) {
+ return asset.getNonValidReason();
+ }
+ };
+
+ private final String name;
+
+ AssetAttribute(String name) {
+ this.name = name;
+ }
+
+ abstract Object read(HMStacAsset asset);
+
+ public static AssetAttribute fromName(String name) {
+ if (name == null || name.isBlank()) {
+ throw new IllegalArgumentException("Asset attribute name cannot be empty");
+ }
+
+ for(AssetAttribute attribute : values()) {
+ if (attribute.name.equalsIgnoreCase(name.trim())) {
+ return attribute;
+ }
+ }
+
+ throw new IllegalArgumentException("Unsupported HMStacAsset attribute: " + name
+ + ". Supported attributes are: id, title, type, valid, epsg, nonValidReason");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACUtils.java b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACUtils.java
index f0856fa005..0239e6f73f 100644
--- a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACUtils.java
+++ b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACUtils.java
@@ -11,7 +11,7 @@
import org.integratedmodelling.klab.api.provenance.IArtifact.Type;
import org.integratedmodelling.klab.exceptions.KlabResourceAccessException;
import org.integratedmodelling.klab.utils.DOIReader;
-import org.opengis.feature.simple.SimpleFeature;
+import org.geotools.api.feature.simple.SimpleFeature;
import com.fasterxml.jackson.core.JsonParseException;
@@ -32,13 +32,14 @@ public static String readKeywords(JSONObject json) {
return null;
}
List keywords = json.getJSONArray("keywords").toList();
- return keywords.isEmpty() ? null :
- keywords.stream().collect(Collectors.joining(","));
+ return keywords.isEmpty() ? null : keywords.stream().collect(Collectors.joining(","));
}
- final private static Set DOI_KEYS_IN_STAC_JSON = Set.of("sci:doi", "assets.sci:doi", "summaries.sci:doi", "properties.sci:doi", "item_assets.sci:doi");
+ final private static Set DOI_KEYS_IN_STAC_JSON = Set.of("sci:doi", "assets.sci:doi", "summaries.sci:doi",
+ "properties.sci:doi", "item_assets.sci:doi");
public static String readDOI(JSONObject json) {
- Optional doi = DOI_KEYS_IN_STAC_JSON.stream().filter(key -> json.has(key)).map(key -> json.getString(key)).findFirst();
+ Optional doi = DOI_KEYS_IN_STAC_JSON.stream().filter(key -> json.has(key)).map(key -> json.getString(key))
+ .findFirst();
return doi.isPresent() ? doi.get() : null;
}
@@ -61,13 +62,13 @@ public static String readDOIAuthors(String doi) {
*/
public static boolean containsLinkTo(JSONObject data, String rel) {
return data.getJSONArray("links").toList().stream()
- .anyMatch(link -> ((JSONObject)link).getString("rel").equalsIgnoreCase(rel));
+ .anyMatch(link -> ((JSONObject) link).getString("rel").equalsIgnoreCase(rel));
}
public static Optional getLinkTo(JSONObject data, String rel) {
return data.getJSONArray("links").toList().stream()
- .filter(link -> ((JSONObject)link).getString("rel").equalsIgnoreCase(rel))
- .map(link -> ((JSONObject)link).getString("href")).findFirst();
+ .filter(link -> ((JSONObject) link).getString("rel").equalsIgnoreCase(rel))
+ .map(link -> ((JSONObject) link).getString("href")).findFirst();
}
public static JSONObject requestMetadata(String collectionUrl, String type) {
@@ -87,7 +88,7 @@ public static String readLicense(JSONObject collection) {
return null;
}
JSONArray links = collection.getJSONArray("links");
- for (int i = 0; i < links.length(); i++) {
+ for(int i = 0; i < links.length(); i++) {
JSONObject link = links.getJSONObject(i);
if (!link.has("rel") || !link.getString("rel").equals("license")) {
continue;
@@ -121,11 +122,12 @@ public static Type inferValueType(String key) {
public static String getCatalogUrl(String collectionUrl, String collectionId, JSONObject collectionData) {
// The URL of the catalog is the root
if (!collectionData.has("links")) {
- throw new KlabResourceAccessException("STAC collection is missing links. It is not fully complaiant and cannot be accessed by the adapter.");
+ throw new KlabResourceAccessException(
+ "STAC collection is missing links. It is not fully complaiant and cannot be accessed by the adapter.");
}
JSONArray links = collectionData.getJSONArray("links");
Optional rootLink = links.toList().stream()
- .filter(link -> ((JSONObject)link).getString("rel").equalsIgnoreCase("root")).findFirst();
+ .filter(link -> ((JSONObject) link).getString("rel").equalsIgnoreCase("root")).findFirst();
if (rootLink.isEmpty()) {
throw new KlabResourceAccessException("STAC collection is missing a relationship to the root catalog");
}
diff --git a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACValidator.java b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACValidator.java
index a01646ac05..3e3f0d6e66 100644
--- a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACValidator.java
+++ b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/STACValidator.java
@@ -7,6 +7,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Predicate;
import org.integratedmodelling.kim.api.IParameters;
import org.integratedmodelling.klab.api.data.IGeometry;
@@ -20,11 +21,13 @@
import org.integratedmodelling.klab.api.runtime.monitoring.IMonitor;
import org.integratedmodelling.klab.data.resources.Resource;
import org.integratedmodelling.klab.data.resources.ResourceBuilder;
+import org.integratedmodelling.klab.exceptions.KlabIllegalArgumentException;
import org.integratedmodelling.klab.exceptions.KlabUnimplementedException;
import org.integratedmodelling.klab.rest.CodelistReference;
import org.integratedmodelling.klab.rest.MappingReference;
import org.integratedmodelling.klab.rest.ResourceCRUDRequest;
import org.integratedmodelling.klab.utils.Pair;
+import org.integratedmodelling.klab.utils.s3.S3URLUtils;
import kong.unirest.json.JSONObject;
@@ -43,55 +46,122 @@ public Builder validate(String urn, URL url, IParameters userData, IMoni
String collectionUrl = userData.get("collection", String.class);
String collectionId = userData.get("collectionId", String.class);
JSONObject collectionData = STACUtils.requestMetadata(collectionUrl, "collection");
- if (collectionId == null) {
+ if (collectionId == null) {
collectionId = collectionData.getString("id");
userData.put("collectionId", collectionId);
}
- IGeometry geometry = STACCollectionParser.readGeometry(collectionData);
- Builder builder = new ResourceBuilder(urn)
- .withParameters(userData)
- .withGeometry(geometry)
- .withType(Type.OBJECT);
+ IGeometry geometry = null;
+ if (!userData.contains("asset") && !userData.contains("jsonSelector")) {
+ geometry = STACCollectionParser.readGeometry(collectionData, true);
+ } else {
+ geometry = STACCollectionParser.readGeometry(collectionData, false);
+ }
+ Builder builder = new ResourceBuilder(urn).withParameters(userData).withGeometry(geometry).withType(Type.OBJECT);
- // The default URL of the resource is the collection endpoint. May be overwritten.
+ // The default URL of the resource is the collection endpoint. May be overwritten.
builder.withMetadata(IMetadata.DC_URL, collectionUrl);
+ JSONObject assetNode;
+
if (userData.contains("asset")) {
- String assetId = userData.get("asset", String.class);
- JSONObject assets = STACCollectionParser.readAssetsFromCollection(collectionUrl, collectionData);
- JSONObject asset = STACAssetMapParser.getAsset(assets, assetId);
-
- Type type = readRasterDataType(asset);
- // Currently, only files:values is supported. If needed, the classification extension could be used too.
- Map vals = STACAssetParser.getFileValues(asset);
- if (!vals.isEmpty()) {
- CodelistReference codelist = populateCodelist(assetId, vals);
- if (type == null) {
- type = codelist.getType();
- }
- builder.addCodeList(codelist);
+ if(userData.contains("cog")) {
+ throw new KlabIllegalArgumentException("STAC asset and cog URL both shouldn't be provided while importing");
+ }
+ String requestedAssetId = userData.get("asset", String.class);
+ assetNode = STACCollectionParser.readAssetInformationFromCollection(collectionUrl, collectionData, requestedAssetId);
+
+ } else if (userData.contains("jsonSelector")) {
+ if (!userData.contains("jsonValue")) {
+ throw new KlabIllegalArgumentException("Both jsonSelector and jsonValue must be provided");
}
- if (type != null) {
- builder.withType(type);
+
+ if(userData.contains("cog")) {
+ throw new KlabIllegalArgumentException("jsonSelector and jsonValue shouldn't be provided along with cog URL while importing");
+ }
+
+ Predicate predicate = STACPathExpression.STACAssetPredicate
+ .fromKongJsonObject(userData.get("jsonSelector", String.class), userData.get("jsonValue", String.class));
+
+ assetNode = STACCollectionParser.readAssetInformationFromCollection(collectionUrl, collectionData, predicate);
+
+ } else {
+ // Just import Features
+ monitor.info("import STAC Collection for Features");
+ readMetadata(collectionData, builder);
+ return builder;
+ }
+
+ String assetId = assetNode.keys().next();
+ JSONObject asset = assetNode.getJSONObject(assetId);
+ String href = asset.getString("href");
+ if (S3URLUtils.isS3Endpoint(href)) {
+ if (href.contains("waw3")) {
+ userData.put("s3EndpointUrl", "https://s3.waw3-1.cloudferro.com");
+ } else if (href.contains("waw4")) {
+ userData.put("s3EndpointUrl", "https://s3.waw4-1.cloudferro.com");
+ } else if (collectionUrl.contains("https://stac.dataspace.copernicus.eu/")){
+ userData.put("s3EndpointUrl", "https://eodata.dataspace.copernicus.eu/");
+ } else {
+ userData.put("s3EndpointUrl", "unknown");
+ }
+
+ }
+
+ Type type = readRasterDataType(asset);
+ // Currently, only files:values is supported. If needed, the classification extension could
+ // be used too.
+ Map vals = STACAssetParser.getFileValues(asset);
+ if (!vals.isEmpty()) {
+ CodelistReference codelist = populateCodelist(assetId, vals);
+ if (type == null) {
+ type = codelist.getType();
}
+ builder.addCodeList(codelist);
+ }
+ if (type != null) {
+ builder.withType(type);
}
-
+ generateCodeList(builder, assetId, asset);
+
if (userData.contains("cog")) {
- if (userData.get("cog") != null) {
- builder.withType(Type.NUMBER);
- }
+ if (userData.get("cog") != null) {
+ builder.withType(Type.NUMBER);
+ }
}
readMetadata(collectionData, builder);
return builder;
}
+ private void generateCodeList(Builder builder, String assetId, JSONObject asset) {
+ Type type = readRasterDataType(asset);
+ // Currently, only files:values is supported. If needed, the classification extension could
+ // be used too.
+ Map vals = STACAssetParser.getFileValues(asset);
+ if (!vals.isEmpty()) {
+ CodelistReference codelist = populateCodelist(assetId, vals);
+ if (type == null) {
+ type = codelist.getType();
+ }
+ builder.addCodeList(codelist);
+ }
+ if (type != null) {
+ builder.withType(type);
+ }
+ }
+
private Type readRasterDataType(JSONObject asset) {
+
+ if (asset.has("type")) {
+ if(asset.get("type").toString().contains("image/tiff")) { // Numbers
+ return Type.NUMBER;
+ }
+ }
if (!asset.has("raster:bands")) {
return null;
}
-
+
if (asset.getJSONArray("raster:bands").isEmpty()
|| !asset.getJSONArray("raster:bands").getJSONObject(0).has("data_type")) {
// We assume that most rasters are numeric. When in doubt, we set the default to Number
@@ -99,7 +169,8 @@ private Type readRasterDataType(JSONObject asset) {
}
String type = asset.getJSONArray("raster:bands").getJSONObject(0).getString("data_type");
// https://github.com/stac-extensions/raster?tab=readme-ov-file#data-types
- final Set NUMERIC_DATA_TYPES = Set.of("int8", "int16", "int32", "int64", "uint8", "unit16", "uint32", "uint64", "float16", "float32", "float64");
+ final Set NUMERIC_DATA_TYPES = Set.of("int8", "int16", "int32", "int64", "uint8", "unit16", "uint32", "uint64",
+ "float16", "float32", "float64");
if (NUMERIC_DATA_TYPES.contains(type)) {
return Type.NUMBER;
}
@@ -121,8 +192,8 @@ private CodelistReference populateCodelist(String assetId, Map v
MappingReference direct = new MappingReference();
MappingReference inverse = new MappingReference();
vals.entrySet().forEach(code -> {
- direct.getMappings().add(new Pair<>(code.getKey(), (String)code.getValue()));
- codelist.getCodeDescriptions().put(code.getKey(), (String)code.getValue());
+ direct.getMappings().add(new Pair<>(code.getKey(), (String) code.getValue()));
+ codelist.getCodeDescriptions().put(code.getKey(), (String) code.getValue());
});
Type type = STACUtils.inferValueType(vals.entrySet().stream().findFirst().get().getKey());
codelist.setType(type);
@@ -132,7 +203,8 @@ private CodelistReference populateCodelist(String assetId, Map v
}
private void readMetadata(final JSONObject json, Builder builder) {
- // We could check the doi only if the Scientific Notation extension is provided, but we can try anyway
+ // We could check the doi only if the Scientific Notation extension is provided, but we can
+ // try anyway
String doi = STACUtils.readDOI(json);
if (doi != null && !doi.isBlank()) {
builder.withMetadata(IMetadata.DC_URL, doi);
@@ -199,7 +271,7 @@ public Collection getAllFilesForResource(File file) {
}
@Override
- public Map extends String, ? extends Object> describeResource(IResource resource) {
+ public Map< ? extends String, ? extends Object> describeResource(IResource resource) {
// TODO Auto-generated method stub
return null;
}
diff --git a/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/extensions/STACFeatureExtension.java b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/extensions/STACFeatureExtension.java
new file mode 100644
index 0000000000..b422e81225
--- /dev/null
+++ b/adapters/klab.ogc/src/main/java/org/integratedmodelling/klab/stac/extensions/STACFeatureExtension.java
@@ -0,0 +1,118 @@
+package org.integratedmodelling.klab.stac.extensions;
+
+import java.io.IOException;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.geotools.api.data.FeatureSource;
+import org.geotools.data.geojson.GeoJSONReader;
+import org.geotools.data.memory.MemoryDataStore;
+import org.integratedmodelling.klab.stac.STACUtils;
+import org.geotools.api.feature.simple.SimpleFeature;
+import org.geotools.api.feature.simple.SimpleFeatureType;
+import org.hortonmachine.gears.io.stac.HMStacItem;
+
+import kong.unirest.HttpResponse;
+import kong.unirest.JsonNode;
+import kong.unirest.Unirest;
+import kong.unirest.json.JSONArray;
+import kong.unirest.json.JSONObject;
+import org.integratedmodelling.klab.api.observations.scale.IScale;
+import org.integratedmodelling.klab.api.observations.scale.space.IEnvelope;
+import org.integratedmodelling.klab.api.observations.scale.space.IGrid;
+import org.integratedmodelling.klab.api.observations.scale.time.ITimeInstant;
+import org.integratedmodelling.klab.stac.STACUtils;
+import java.time.format.DateTimeFormatter;
+import java.time.*;
+
+public class STACFeatureExtension {
+ public static FeatureSource getFeatures(JSONObject catalogData, String collectionId, List bbox, ITimeInstant start, ITimeInstant end) throws Exception {
+
+ String searchEndpoint = STACUtils.getLinkTo(catalogData, "search")
+ .orElseThrow(() -> new Exception("Search Link not found for the Catalog"));
+
+ List featureList = new ArrayList<>();
+
+ JSONArray bboxArray = new JSONArray();
+ for (Double v : bbox) {
+ bboxArray.put(v);
+ }
+
+
+ JSONObject searchPayload = new JSONObject()
+ .put("limit", 1000)
+ .put("bbox", bboxArray)
+ .put("collections", new JSONArray().put(collectionId));
+
+ while (searchEndpoint != null) {
+
+ HttpResponse response = Unirest
+ .post(searchEndpoint)
+ .header("Content-Type", "application/json")
+ .body(searchPayload)
+ .asJson();
+
+ JSONObject body = response.getBody().getObject();
+ JSONArray features = body.getJSONArray("features");
+
+ Iterator
\ No newline at end of file
diff --git a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/adapters/GridAdapter.java b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/adapters/GridAdapter.java
index 79e4281eaa..7d41fa927b 100644
--- a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/adapters/GridAdapter.java
+++ b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/adapters/GridAdapter.java
@@ -10,7 +10,7 @@
import java.util.Map;
import java.util.Set;
-import org.geotools.data.simple.SimpleFeatureSource;
+import org.geotools.api.data.SimpleFeatureSource;
import org.geotools.feature.FeatureIterator;
import org.geotools.grid.Grids;
import org.integratedmodelling.klab.Configuration;
@@ -44,7 +44,7 @@
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.Serializer;
-import org.opengis.feature.simple.SimpleFeature;
+import org.geotools.api.feature.simple.SimpleFeature;
@UrnAdapter(type = GridAdapter.NAME, version = Version.CURRENT)
public class GridAdapter implements IUrnAdapter {
diff --git a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/BarnesSurfaceResolver.java b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/BarnesSurfaceResolver.java
index 7717a3d9ee..e2a1bb207d 100644
--- a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/BarnesSurfaceResolver.java
+++ b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/BarnesSurfaceResolver.java
@@ -48,12 +48,12 @@
import org.integratedmodelling.klab.utils.Parameters;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Point;
-import org.opengis.feature.simple.SimpleFeatureType;
-import org.opengis.geometry.Envelope;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.crs.GeographicCRS;
-import org.opengis.referencing.crs.ProjectedCRS;
-import org.opengis.referencing.datum.Ellipsoid;
+import org.geotools.api.feature.simple.SimpleFeatureType;
+import org.geotools.api.geometry.Bounds;
+import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
+import org.geotools.api.referencing.crs.GeographicCRS;
+import org.geotools.api.referencing.crs.ProjectedCRS;
+import org.geotools.api.referencing.datum.Ellipsoid;
public class BarnesSurfaceResolver extends AbstractContextualizer implements IResolver, IExpression {
@@ -87,7 +87,7 @@ public IState resolve(IState target, IContextualizationScope context) throws Kla
grid.getEast(), grid.getWest(), (int) grid.getXCells(), (int) grid.getYCells(),
grid.getProjection().getCoordinateReferenceSystem());
- Envelope env = inInterpolationGrid.getEnvelope();
+ Bounds env = inInterpolationGrid.getEnvelope();
CoordinateReferenceSystem Crs = grid.getProjection().getCoordinateReferenceSystem();
diff --git a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/CustomBarnesSurfaceInterpolator.java b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/CustomBarnesSurfaceInterpolator.java
index aef6fb5976..6c100f27a2 100644
--- a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/CustomBarnesSurfaceInterpolator.java
+++ b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/CustomBarnesSurfaceInterpolator.java
@@ -22,8 +22,8 @@
import org.geotools.referencing.GeodeticCalculator;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.datum.Ellipsoid;
+import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
+import org.geotools.api.referencing.datum.Ellipsoid;
/**
* Interpolates a surface across a regular grid from an irregular set of data points using the
diff --git a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/KrigingResolver.java b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/KrigingResolver.java
index 30ceb2e77e..3484285c14 100644
--- a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/KrigingResolver.java
+++ b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/core/KrigingResolver.java
@@ -13,7 +13,7 @@
import org.hortonmachine.gears.libs.modules.HMConstants;
import org.hortonmachine.gears.utils.coverage.CoverageUtilities;
import org.hortonmachine.gears.utils.math.matrixes.MatrixException;
-import org.hortonmachine.hmachine.modules.statistics.kriging.old.OmsKriging;
+import org.hortonmachine.hmachine.modules.statistics.krigingexp.old.OmsKriging;
import org.integratedmodelling.geoprocessing.TaskMonitor;
import org.integratedmodelling.kim.api.IKimConcept;
import org.integratedmodelling.klab.Concepts;
@@ -39,7 +39,7 @@
import org.integratedmodelling.klab.utils.NumberUtils;
import org.integratedmodelling.klab.utils.Parameters;
import org.locationtech.jts.geom.Point;
-import org.opengis.feature.simple.SimpleFeatureType;
+import org.geotools.api.feature.simple.SimpleFeatureType;
public class KrigingResolver extends AbstractContextualizer implements IResolver, IExpression {
diff --git a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/hydrology/MarineFloodResolver.java b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/hydrology/MarineFloodResolver.java
index a99c1bb5c2..5919420165 100644
--- a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/hydrology/MarineFloodResolver.java
+++ b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/hydrology/MarineFloodResolver.java
@@ -3,11 +3,9 @@
import static org.hortonmachine.gears.libs.modules.HMConstants.floatNovalue;
import java.awt.image.DataBuffer;
-
-import javax.media.jai.iterator.RandomIter;
+import java.util.ArrayList;
import org.geotools.coverage.grid.GridCoverage2D;
-import org.geotools.coverage.util.CoverageUtilities;
import org.hortonmachine.gears.libs.modules.HMRaster;
import org.hortonmachine.gears.utils.RegionMap;
import org.integratedmodelling.klab.api.data.general.IExpression;
@@ -20,10 +18,6 @@
import org.integratedmodelling.klab.exceptions.KlabException;
import org.integratedmodelling.klab.utils.Parameters;
-import oms3.gen.doubleAccess;
-
-import java.util.ArrayList;
-
public class MarineFloodResolver extends AbstractContextualizer implements IResolver, IExpression {
private Double decayFact;
diff --git a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/hydrology/StreamOutletInstantiator.java b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/hydrology/StreamOutletInstantiator.java
index 4229b664b3..7bfae9f4d5 100644
--- a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/hydrology/StreamOutletInstantiator.java
+++ b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/hydrology/StreamOutletInstantiator.java
@@ -9,9 +9,8 @@
import java.util.Comparator;
import java.util.List;
-import javax.media.jai.iterator.RandomIter;
-import javax.media.jai.iterator.RandomIterFactory;
-
+import org.eclipse.imagen.iterator.RandomIter;
+import org.eclipse.imagen.iterator.RandomIterFactory;
import org.hortonmachine.gears.libs.modules.FlowNode;
import org.hortonmachine.hmachine.modules.demmanipulation.markoutlets.OmsMarkoutlets;
import org.integratedmodelling.geoprocessing.TaskMonitor;
diff --git a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/morphology/MaximaFinderInstantiator.java b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/morphology/MaximaFinderInstantiator.java
index 6a77f0e8c8..84fb4c8db5 100644
--- a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/morphology/MaximaFinderInstantiator.java
+++ b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/morphology/MaximaFinderInstantiator.java
@@ -31,7 +31,7 @@
import org.integratedmodelling.klab.rest.StateSummary;
import org.integratedmodelling.klab.scale.Scale;
import org.integratedmodelling.klab.utils.Parameters;
-import org.opengis.feature.simple.SimpleFeature;
+import org.geotools.api.feature.simple.SimpleFeature;
public class MaximaFinderInstantiator extends AbstractContextualizer implements IInstantiator, IExpression {
diff --git a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/remotesensing/SentinelResolver.java b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/remotesensing/SentinelResolver.java
index 9c5b9aac1d..1335ac7188 100644
--- a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/remotesensing/SentinelResolver.java
+++ b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/remotesensing/SentinelResolver.java
@@ -12,7 +12,7 @@
import org.hortonmachine.gears.io.stac.HMStacItem;
import org.hortonmachine.gears.io.stac.HMStacManager;
import org.hortonmachine.gears.libs.modules.HMRaster;
-import org.hortonmachine.gears.utils.CrsUtilities;
+import org.hortonmachine.gears.utils.crs.CrsUtilities;
import org.hortonmachine.gears.utils.RegionMap;
import org.hortonmachine.gears.utils.geometry.GeometryUtilities;
import org.integratedmodelling.geoprocessing.TaskMonitor;
@@ -34,7 +34,7 @@
import org.integratedmodelling.klab.utils.Parameters;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Polygon;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
public class SentinelResolver extends AbstractContextualizer implements IResolver, IExpression {
diff --git a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/viewshed/ViewshedResolver.java b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/viewshed/ViewshedResolver.java
index 8c6a47e6cc..144f345be1 100644
--- a/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/viewshed/ViewshedResolver.java
+++ b/components/klab.component.hydrology/src/main/java/org/integratedmodelling/geoprocessing/viewshed/ViewshedResolver.java
@@ -29,7 +29,7 @@
import org.integratedmodelling.klab.utils.Parameters;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Point;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
public class ViewshedResolver extends AbstractContextualizer implements IResolver, IExpression {
diff --git a/components/klab.component.tables/pom.xml b/components/klab.component.tables/pom.xml
index b2066517c0..93196a16c8 100644
--- a/components/klab.component.tables/pom.xml
+++ b/components/klab.component.tables/pom.xml
@@ -36,9 +36,20 @@
- tech.tablesaw
- tablesaw-excel
- 0.38.5
+ tech.tablesaw
+ tablesaw-excel
+ 0.38.5
+
+
+ org.apache.poi
+ poi-ooxml
+
+
+
+
+
+ org.apache.poi
+ poi-ooxml
@@ -76,3 +87,4 @@
+
diff --git a/components/klab.components.cdm/src/main/java/org/integratedmodelling/cdm/utils/NetCDFUtils.java b/components/klab.components.cdm/src/main/java/org/integratedmodelling/cdm/utils/NetCDFUtils.java
index dfb3debdfb..8b9f36231e 100644
--- a/components/klab.components.cdm/src/main/java/org/integratedmodelling/cdm/utils/NetCDFUtils.java
+++ b/components/klab.components.cdm/src/main/java/org/integratedmodelling/cdm/utils/NetCDFUtils.java
@@ -25,8 +25,11 @@
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.media.jai.RasterFactory;
-
+import org.eclipse.imagen.RasterFactory;
+import org.geotools.api.feature.simple.SimpleFeatureType;
+import org.geotools.api.parameter.GeneralParameterValue;
+import org.geotools.api.parameter.ParameterValueGroup;
+import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.io.AbstractGridFormat;
@@ -46,10 +49,6 @@
import org.integratedmodelling.klab.exceptions.KlabIllegalArgumentException;
import org.integratedmodelling.klab.exceptions.KlabUnsupportedFeatureException;
import org.integratedmodelling.klab.utils.NumberUtils;
-import org.opengis.feature.simple.SimpleFeatureType;
-import org.opengis.parameter.GeneralParameterValue;
-import org.opengis.parameter.ParameterValueGroup;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.beust.jcommander.internal.Lists;
diff --git a/ide/org.integratedmodelling.klab.ide/src/org/integratedmodelling/klab/ide/views/ResourceEditor.java b/ide/org.integratedmodelling.klab.ide/src/org/integratedmodelling/klab/ide/views/ResourceEditor.java
index 5d64c3f114..7cdf4406f6 100644
--- a/ide/org.integratedmodelling.klab.ide/src/org/integratedmodelling/klab/ide/views/ResourceEditor.java
+++ b/ide/org.integratedmodelling.klab.ide/src/org/integratedmodelling/klab/ide/views/ResourceEditor.java
@@ -560,6 +560,9 @@ public void loadResource(ResourceReference resource) {
private File getResourcePath(ResourceReference resource) {
String path = resource.getLocalPath();
+ if (path == null) {
+ return null;
+ }
String project = Path.getFirst(path, "/");
IKimProject prj = Kim.INSTANCE.getProject(project);
if (project != null) {
diff --git a/kactors/org.integratedmodelling.kactors/build.properties b/kactors/org.integratedmodelling.kactors/build.properties
index aa338a94e6..c324a919eb 100644
--- a/kactors/org.integratedmodelling.kactors/build.properties
+++ b/kactors/org.integratedmodelling.kactors/build.properties
@@ -15,6 +15,5 @@ additional.bundles = org.eclipse.xtext.xbase,\
org.eclipse.emf.mwe2.launch,\
org.eclipse.emf.mwe2.lib,\
org.objectweb.asm,\
- org.apache.commons.logging,\
org.apache.log4j,\
com.ibm.icu
diff --git a/kdl/org.integratedmodelling.kdl/build.properties b/kdl/org.integratedmodelling.kdl/build.properties
index 18d540bf6c..a7b32375b7 100644
--- a/kdl/org.integratedmodelling.kdl/build.properties
+++ b/kdl/org.integratedmodelling.kdl/build.properties
@@ -15,6 +15,5 @@ additional.bundles = org.eclipse.xtext.xbase,\
org.eclipse.emf.mwe2.launch,\
org.eclipse.emf.mwe2.lib,\
org.objectweb.asm,\
- org.apache.commons.logging,\
org.apache.log4j,\
com.ibm.icu
diff --git a/kim/org.integratedmodelling.kim/build.properties b/kim/org.integratedmodelling.kim/build.properties
index 2072401d98..05568542e3 100644
--- a/kim/org.integratedmodelling.kim/build.properties
+++ b/kim/org.integratedmodelling.kim/build.properties
@@ -13,6 +13,5 @@ additional.bundles = org.eclipse.xtext.xbase,\
org.eclipse.emf.mwe2.launch,\
org.eclipse.emf.mwe2.lib,\
org.objectweb.asm,\
- org.apache.commons.logging,\
org.apache.log4j,\
com.ibm.icu
\ No newline at end of file
diff --git a/klab.authentication/pom.xml b/klab.authentication/pom.xml
index 25a62c91b4..c4189aab04 100644
--- a/klab.authentication/pom.xml
+++ b/klab.authentication/pom.xml
@@ -36,7 +36,6 @@
commons-io
commons-io
- 2.14.0
diff --git a/klab.authentication/src/main/java/org/integratedmodelling/klab/Authentication.java b/klab.authentication/src/main/java/org/integratedmodelling/klab/Authentication.java
index 2d980bbdfc..9c77612615 100644
--- a/klab.authentication/src/main/java/org/integratedmodelling/klab/Authentication.java
+++ b/klab.authentication/src/main/java/org/integratedmodelling/klab/Authentication.java
@@ -463,7 +463,7 @@ public ExternalAuthenticationCredentials getCredentials(String endpoint) {
* @return the credential or null if not present
*/
private ExternalAuthenticationCredentials getCredentialsByUrl(String hostUrl) {
- return externalCredentials.values().stream().filter(credential -> credential.getURL().equals(hostUrl)).sorted()
+ return externalCredentials.values().stream().filter(credential -> credential.getURL().equals(hostUrl))
.findFirst().orElse(null);
}
diff --git a/klab.authentication/src/main/java/org/integratedmodelling/klab/communication/client/Client.java b/klab.authentication/src/main/java/org/integratedmodelling/klab/communication/client/Client.java
index 512530c3f1..9655a693a1 100644
--- a/klab.authentication/src/main/java/org/integratedmodelling/klab/communication/client/Client.java
+++ b/klab.authentication/src/main/java/org/integratedmodelling/klab/communication/client/Client.java
@@ -76,23 +76,25 @@
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
+import org.springframework.web.client.UnknownContentTypeException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
- * Helper to avoid having to write 10 lines every time I need to do a GET with headers. It can be
- * given authorization objects using the with(...) idiom:
+ * Helper to avoid having to write 10 lines every time I need to do a GET with
+ * headers. It can be given authorization objects using the with(...) idiom:
*
* RestTemplateHelper template = new RestTemplateHelper(); ...
* template.with(session).post(....) ...
*
- * with(..) can be called with any {@link IIdentity} according to the type of authentication
- * required by the receiver. Automatically sets the necessary tokens and parameters.
+ * with(..) can be called with any {@link IIdentity} according to the type of
+ * authentication required by the receiver. Automatically sets the necessary
+ * tokens and parameters.
*
- * The template also configures an objectmapper for optimal use in k.LAB, manages errors and inserts
- * user agent headers.
+ * The template also configures an objectmapper for optimal use in k.LAB,
+ * manages errors and inserts user agent headers.
*
*
* @author ferdinando.villa
@@ -100,677 +102,684 @@
*/
public class Client extends RestTemplate implements IClient {
- public static final String KLAB_VERSION_HEADER = "KlabVersion";
- public static final String KLAB_CONNECTION_TIMEOUT = "klab.connection.timeout";
-
- ObjectMapper objectMapper;
- String authorizationToken;
- String authenticationToken;
- MediaType contentType = new MediaType(MediaType.APPLICATION_JSON, StandardCharsets.UTF_8);
- RestTemplate basicTemplate;
- private Set endpoints = new HashSet<>();
-
- private static ClientHttpRequestFactory factory;
-
- public static Client createCustomTimeoutClient(int timeout) {
- HttpComponentsClientHttpRequestFactory custom = new HttpComponentsClientHttpRequestFactory();
- custom.setReadTimeout(timeout);
- custom.setConnectTimeout(timeout);
- return new Client(custom);
- }
-
- public static synchronized Client create() {
-
- if (factory == null) {
- factory = new HttpComponentsClientHttpRequestFactory();
- if (Configuration.INSTANCE.getProperties().containsKey(KLAB_CONNECTION_TIMEOUT)) {
- int connectTimeout = 1000
- * Integer.parseInt(Configuration.INSTANCE.getProperties().getProperty(KLAB_CONNECTION_TIMEOUT));
- ((HttpComponentsClientHttpRequestFactory) factory).setReadTimeout(connectTimeout);
- ((HttpComponentsClientHttpRequestFactory) factory).setConnectTimeout(connectTimeout);
- }
- }
-
- return new Client(factory);
- }
-
- public static Client createJson() {
- Client ret = create();
- ret.setup();
- return ret;
- }
-
- /**
- * This one will return a client that interprets anything as JSON independent of content type.
- * Required for misbehaving services that return JSON with other content types.
- *
- * @return
- */
- public static synchronized Client createUniversalJSON() {
-
- if (factory == null) {
- factory = new HttpComponentsClientHttpRequestFactory();
- if (Configuration.INSTANCE.getProperties().containsKey(KLAB_CONNECTION_TIMEOUT)) {
- int connectTimeout = 1000
- * Integer.parseInt(Configuration.INSTANCE.getProperties().getProperty(KLAB_CONNECTION_TIMEOUT));
- ((HttpComponentsClientHttpRequestFactory) factory).setReadTimeout(connectTimeout);
- ((HttpComponentsClientHttpRequestFactory) factory).setConnectTimeout(connectTimeout);
- }
- }
-
- return new UniversalClient(factory);
- }
-
- public static class UniversalClient extends Client {
- UniversalClient(ClientHttpRequestFactory factory) {
- super(factory);
- objectMapper = new ObjectMapper();
- objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- List> messageConverters = new ArrayList>();
- MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
- /*
- * Make this converter process any kind of response, not only application/*json, which
- * is the default behaviour
- */
- converter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
- messageConverters.add(converter);
- this.setMessageConverters(messageConverters);
- }
- }
-
- public static class NodeClient extends Client {
-
- public NodeClient(INodeIdentity node) {
-
- super(factory);
-
- objectMapper = new ObjectMapper();
- List> messageConverters = new ArrayList<>();
- MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
- StringHttpMessageConverter utf8 = new StringHttpMessageConverter(Charset.forName("UTF-8"));
- FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
- ProtobufHttpMessageConverter protobufConverter = new ProtobufHttpMessageConverter();
- // ByteArrayHttpMessageConverter byteConverter = new
- // ByteArrayHttpMessageConverter();
- jsonMessageConverter.setObjectMapper(objectMapper);
-
- setErrorHandler(new JSONResponseErrorHandler());
- messageConverters.add(jsonMessageConverter);
- messageConverters.add(utf8);
- messageConverters.add(formHttpMessageConverter);
- messageConverters.add(protobufConverter);
- // messageConverters.add(byteConverter);
- setMessageConverters(messageConverters);
- this.setInterceptors(Collections.singletonList(new AuthorizationInterceptor()));
- this.authorizationToken = node.getId();
- }
- }
-
- /**
- * Send an authentication request to a hub for an engine.
- *
- * @param url
- * @param request
- * @return the response. If not authenticated, throw a KlabAuthorizationException. If timeout,
- * return null.
- */
- public EngineAuthenticationResponse authenticateEngine(String url, EngineAuthenticationRequest request) {
- return post(url + API.HUB.AUTHENTICATE_ENGINE, request, EngineAuthenticationResponse.class);
- }
-
- /**
- * Send an authentication request to a hub for a node.
- *
- * @param url
- * @param request
- * @return the response. If not authenticated, throw a KlabAuthorizationException. If timeout,
- * return null.
- */
- public NodeAuthenticationResponse authenticateNode(String url, NodeAuthenticationRequest request) {
- return post(url + API.HUB.AUTHENTICATE_NODE, request, NodeAuthenticationResponse.class);
- }
-
- /**
- * Check an engine's heartbeat.
- *
- * @param url base engine/node URL
- * @return true if alive
- */
- public boolean ping(String url, String endpoint) {
- try {
- ResponseEntity