From a15a9131fb36c2e539dd3babf4fc161b5db702fc Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Tue, 2 May 2023 15:47:33 -0700 Subject: [PATCH 01/11] cadc-dali: port caom2 polygon validation and calculation code --- cadc-dali/build.gradle | 2 +- .../ca/nrc/cadc/dali/CartesianTransform.java | 348 +++++++++++++++++ .../cadc/dali/InvalidPolygonException.java | 80 ++++ .../main/java/ca/nrc/cadc/dali/Polygon.java | 356 +++++++++++++++++- .../java/ca/nrc/cadc/dali/PolygonTest.java | 239 ++++++++++++ 5 files changed, 1015 insertions(+), 10 deletions(-) create mode 100644 cadc-dali/src/main/java/ca/nrc/cadc/dali/CartesianTransform.java create mode 100644 cadc-dali/src/main/java/ca/nrc/cadc/dali/InvalidPolygonException.java create mode 100644 cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java diff --git a/cadc-dali/build.gradle b/cadc-dali/build.gradle index 220822cd..d7151a14 100644 --- a/cadc-dali/build.gradle +++ b/cadc-dali/build.gradle @@ -14,7 +14,7 @@ sourceCompatibility = 1.8 group = 'org.opencadc' -version = '1.2.17' +version = '1.2.18' description = 'OpenCADC DALI library' def git_url = 'https://github.com/opencadc/dal' diff --git a/cadc-dali/src/main/java/ca/nrc/cadc/dali/CartesianTransform.java b/cadc-dali/src/main/java/ca/nrc/cadc/dali/CartesianTransform.java new file mode 100644 index 00000000..27a908a4 --- /dev/null +++ b/cadc-dali/src/main/java/ca/nrc/cadc/dali/CartesianTransform.java @@ -0,0 +1,348 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package ca.nrc.cadc.dali; + +import org.apache.log4j.Logger; + +// package access so only usable from Polygon (for now) +class CartesianTransform { + private static final Logger log = Logger.getLogger(CartesianTransform.class); + + // the negative X axis + static final double[] NEG_X_AXIS = new double[] { -1.0, 0.0, 0.0 }; + + // the positive X axis + static final double[] POS_X_AXIS = new double[] { 1.0, 0.0, 0.0 }; + + public static final String X = "x"; + public static final String Y = "y"; + public static final String Z = "z"; + + // some tests mess with these + double angle; + String axis; + + private CartesianTransform() { + } + + public static CartesianTransform getTransform(Polygon poly) { + return getTransform(poly, false); + } + + public static CartesianTransform getTransform(Polygon poly, boolean force) { + double[] cube = getBoundingCube(poly, null); + return getTransform(cube, force); + } + + public static CartesianTransform getTransform(Circle c) { + return getTransform(c, false); + } + + public static CartesianTransform getTransform(Circle c, boolean force) { + double[] xyz = CartesianTransform.toUnitSphere(c.getCenter().getLongitude(), c.getCenter().getLatitude()); + double[] cube = new double[] { + xyz[0], xyz[0], + xyz[1], xyz[1], + xyz[2], xyz[2] + }; + return getTransform(cube, force); + } + + public static CartesianTransform getTransform(double[] cube, + boolean force) { + double x1 = cube[0]; + double x2 = cube[1]; + double y1 = cube[2]; + double y2 = cube[3]; + double z1 = cube[4]; + double z2 = cube[5]; + // log.debug("getTransform: bounding cube = " + x1+ ":" + x2 + " " + y1 + // + ":" + y2 + " " + z1 + ":" + z2); + + double cx = 0.5 * (x1 + x2); + double cz = 0.5 * (z1 + z2); + // log.debug("getTransform: bounding cube center = " + cx + " " + cy + " + // " + cz); + + if (Math.abs(cz) > 0.02 || force) { + CartesianTransform trans = new CartesianTransform(); + trans.axis = CartesianTransform.Y; + // project vector in X-Z plane and normalise + double mv1 = Math.sqrt(cx * cx + cz * cz); + double[] v1 = { cx / mv1, 0, cz / mv1 }; + // acos is only valid for 0 to PI + if (cz > 0.0) { // north + // angle to +X axis, then add 180 degrees + double[] v2 = { 1, 0, 0 }; + double cosa = dotProduct(v1, v2); + trans.angle = Math.acos(cosa) + Math.PI; + } else { // south + // directly get angle to -X axis + double[] v2 = { -1, 0, 0 }; + double cosa = dotProduct(v1, v2); + trans.angle = Math.acos(cosa); + } + log.debug("off equator: " + trans); + return trans; + } + if (y1 <= 0.0 && y2 >= 0.0 && x1 > 0.0) { + CartesianTransform trans = new CartesianTransform(); + trans.angle = Math.PI; + trans.axis = CartesianTransform.Z; + log.debug("straddling meridan at equator: " + trans); + return trans; + } + return new CartesianTransform(); + } + + public CartesianTransform getInverseTransform() { + if (isNull()) { + return this; + } + CartesianTransform ret = new CartesianTransform(); + ret.axis = axis; + ret.angle = -1.0 * angle; + return ret; + } + + @Override + public String toString() { + return "CartesianTransform[" + axis + "," + angle + "]"; + } + + public boolean isNull() { + return axis == null; + } + + /** + * Convert long,lat coordinates to cartesian coordinates on the unit sphere. + * NOTE: this uses a right-handed unit sphere but looking from the outside, + * which is opposite to the normal astro convention of looking from the + * center (which means theta goes in the other direction in astronomy), but + * as long as the opposite conversion is done with toLongLat that is just + * fine. + * + * @param longitude + * @param latitude + * @return a double[3] + */ + public static double[] toUnitSphere(double longitude, double latitude) { + double[] ret = new double[3]; + double theta = Math.toRadians(longitude); + double phi = Math.toRadians(90.0 - latitude); + ret[0] = Math.cos(theta) * Math.sin(phi); + ret[1] = Math.sin(theta) * Math.sin(phi); + ret[2] = Math.cos(phi); + return ret; + } + + /** + * Convert cartesian points on the unit sphere to long,lat. + * + * @param x + * @param y + * @param z + * @return a double[2] + */ + public static double[] toLongLat(double x, double y, double z) { + double[] ret = new double[2]; + if ((x == 0.0 && y == 0.0) || z == 1.0 || z == -1.0) { + ret[0] = 0.0; + } else { + ret[0] = Math.toDegrees(Math.atan2(y, x)); + } + if (ret[0] < 0.0) { + ret[0] += 360.0; + } + if (z > 1.0) { + z = 1.0; + } else if (z < -1.0) { + z = -1.0; + } + ret[1] = 90.0 - Math.toDegrees(Math.acos(z)); + return ret; + } + + public Point transform(Point p) { + if (isNull()) { + return p; + } + + double[] p2 = transformPoint(p); + return new Point(p2[0], p2[1]); + } + + public Polygon transform(Polygon p) { + if (isNull()) { + return p; + } + + Polygon ret = new Polygon(); + for (Point v : p.getVertices()) { + ret.getVertices().add(transform(v)); + } + return ret; + } + + // impl for above methods + private double[] transformPoint(Point p) { + double[] xyz = CartesianTransform.toUnitSphere(p.getLongitude(), p.getLatitude()); + double[] dp = rotate(xyz); + return CartesianTransform.toLongLat(dp[0], dp[1], dp[2]); + } + + public static double[] getBoundingCube(Polygon poly, double[] cube) { + // x1, x2, y1, y2, z1, z2 + if (cube == null) { + cube = new double[6]; + cube[0] = Double.POSITIVE_INFINITY; + cube[1] = Double.NEGATIVE_INFINITY; + cube[2] = Double.POSITIVE_INFINITY; + cube[3] = Double.NEGATIVE_INFINITY; + cube[4] = Double.POSITIVE_INFINITY; + cube[5] = Double.NEGATIVE_INFINITY; + } + for (Point v : poly.getVertices()) { + double[] xyz = CartesianTransform.toUnitSphere(v.getLongitude(), + v.getLatitude()); + cube[0] = Math.min(cube[0], xyz[0]); + cube[1] = Math.max(cube[1], xyz[0]); + + cube[2] = Math.min(cube[2], xyz[1]); + cube[3] = Math.max(cube[3], xyz[1]); + + cube[4] = Math.min(cube[4], xyz[2]); + cube[5] = Math.max(cube[5], xyz[2]); + } + // fix X bounds assuming smallish polygons and cubes + if (cube[2] < 0.0 && 0.0 < cube[3] && cube[4] < 0.0 && 0.0 < cube[5]) { + if (cube[0] > 0.0) { + cube[1] = 1.01; // slightly outside sphere + } else if (cube[1] < 0.0) { + cube[0] = -1.01; // slightly outside sphere + } + } + // fix Y bounds assuming smallish polygons and cubes + if (cube[0] < 0.0 && 0.0 < cube[1] && cube[4] < 0.0 && 0.0 < cube[5]) { + if (cube[2] > 0.0) { + cube[3] = 1.01; // slightly outside sphere + } else if (cube[3] < 0.0) { + cube[2] = -1.01; // slightly outside sphere + } + } + // fix Z bounds assuming smallish polygons and cubes + if (cube[0] < 0.0 && 0.0 < cube[1] && cube[2] < 0.0 && 0.0 < cube[3]) { + if (cube[4] > 0.0) { + cube[5] = 1.01; // slightly outside sphere + } else if (cube[5] < 0.0) { + cube[4] = -1.01; // slightly outside sphere + } + } + return cube; + } + + private static double dotProduct(double[] v1, double[] v2) { + return (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]); + } + + public double[] rotate(double[] p) { + if (Y.equals(axis)) { + return yrotate(p); + } + + if (Z.equals(axis)) { + return zrotate(p); + } + + throw new IllegalStateException("unknown axis: " + axis); + } + + // rotation about the y-axis + private double[] yrotate(double[] p) { + double[] ret = new double[3]; + double cr = Math.cos(angle); + double sr = Math.sin(angle); + ret[0] = cr * p[0] + sr * p[2]; + ret[1] = p[1]; + ret[2] = -1.0 * sr * p[0] + cr * p[2]; + return ret; + } + + // rotation about the z-axis + private double[] zrotate(double[] p) { + double[] ret = new double[3]; + double cr = Math.cos(angle); + double sr = Math.sin(angle); + ret[0] = cr * p[0] + -1 * sr * p[1]; + ret[1] = sr * p[0] + cr * p[1]; + ret[2] = p[2]; + return ret; + } +} diff --git a/cadc-dali/src/main/java/ca/nrc/cadc/dali/InvalidPolygonException.java b/cadc-dali/src/main/java/ca/nrc/cadc/dali/InvalidPolygonException.java new file mode 100644 index 00000000..21246a09 --- /dev/null +++ b/cadc-dali/src/main/java/ca/nrc/cadc/dali/InvalidPolygonException.java @@ -0,0 +1,80 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +************************************************************************ +*/ + +package ca.nrc.cadc.dali; + +/** + * Exception for a polygon that is invalid according to the DALI specification. + * + * @author pdowler + */ +public class InvalidPolygonException extends Exception { + + public InvalidPolygonException(String msg) { + super(msg); + } +} diff --git a/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java b/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java index 36e8e0f0..ff1ca1f7 100644 --- a/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java +++ b/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java @@ -3,7 +3,7 @@ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * -* (c) 2019. (c) 2019. +* (c) 2023. (c) 2023. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 @@ -70,14 +70,25 @@ package ca.nrc.cadc.dali; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import org.apache.log4j.Logger; /** - * + * DALI polygon class with port of CAOM-2.4 polygon validation code. + * * @author pdowler */ public class Polygon implements Shape { - private List vertices = new ArrayList(); + private static final Logger log = Logger.getLogger(Polygon.class); + + private final List points = new ArrayList(); + + // lazily computed + private transient Point center; + private transient Double area; + private transient Circle minimumSpanningCircle; + private transient Boolean ccw; public Polygon() { } @@ -86,7 +97,7 @@ public Polygon() { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Polygon["); - for (Point v : vertices) { + for (Point v : points) { sb.append(v.getLongitude()).append(" ").append(v.getLatitude()).append(" "); } sb.setCharAt(sb.length() - 1, ']'); @@ -94,7 +105,7 @@ public String toString() { } public List getVertices() { - return vertices; + return points; } @Override @@ -104,12 +115,12 @@ public boolean equals(Object obj) { } Polygon rhs = (Polygon) obj; - if (this.vertices.size() != rhs.vertices.size()) { + if (this.points.size() != rhs.points.size()) { return false; } - for (int i = 0; i < vertices.size(); i++) { - Point tp = this.vertices.get(i); - Point rp = rhs.vertices.get(i); + for (int i = 0; i < points.size(); i++) { + Point tp = this.points.get(i); + Point rp = rhs.points.get(i); if (!tp.equals(rp)) { return false; } @@ -117,4 +128,331 @@ public boolean equals(Object obj) { return true; } + /** + * Validate this polygon for conformance to IVOA DALI polygon rules. + * @throws InvalidPolygonException violation of DALI spec + */ + public final void validate() throws InvalidPolygonException { + //validatePoints(); + validateSegments(); + initProps(); + // DALI polygons are always CCW + // unsupported: if we detect CW here it is equivalent to the region + // outside with area = 4*pi - area and larger than half the sphere + if (!ccw) { + throw new InvalidPolygonException("clockwise winding direction"); + } + } + + public Point getCenter() { + if (center == null) { + initProps(); + } + return center; + } + + public double getArea() { + if (area == null) { + initProps(); + } + return area; + } + + public double getSize() { + if (minimumSpanningCircle == null) { + initProps(); + } + return 2.0 * minimumSpanningCircle.getRadius(); + } + + public Circle getMinimumSpanningCircle() { + if (minimumSpanningCircle == null) { + initProps(); + } + return minimumSpanningCircle; + } + + private void initProps() { + PolygonProperties pp = computePolygonProperties(); + this.area = pp.area; + this.center = pp.center; + this.minimumSpanningCircle = pp.minSpanCircle; + this.ccw = pp.windCounterClockwise; + } + + private static class PolygonProperties { + boolean windCounterClockwise; + Double area; + Point center; + Circle minSpanCircle; + } + + private PolygonProperties computePolygonProperties() { + // log.debug("computePolygonProperties: " + poly); + // the transform needed for computing things in long/lat using cartesian + // approximation + CartesianTransform trans = CartesianTransform.getTransform(this); + Polygon tpoly = trans.transform(this); + + // algorithm from + // http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/ + double a = 0.0; + double cx = 0.0; + double cy = 0.0; + int lastMoveTo = 0; // start of simple polygon + Iterator pi = tpoly.getVertices().iterator(); + Point start = pi.next(); + Point v1 = start; + while (pi.hasNext()) { + Point v2 = pi.next(); + //log.warn("props: " + v1 + " " + v2); + double tmp = v1.getLongitude() * v2.getLatitude() - v2.getLongitude() * v1.getLatitude(); + a += tmp; + cx += (v1.getLongitude() + v2.getLongitude()) * tmp; + cy += (v1.getLatitude() + v2.getLatitude()) * tmp; + v1 = v2; + if (!pi.hasNext()) { + v2 = start; + //log.warn("props/implicit close: " + v1 + " " + v2); + tmp = v1.getLongitude() * v2.getLatitude() - v2.getLongitude() * v1.getLatitude(); + a += tmp; + cx += (v1.getLongitude() + v2.getLongitude()) * tmp; + cy += (v1.getLatitude() + v2.getLatitude()) * tmp; + } + } + + //log.warn("raw props: " + cx + "," + cy + " a=" + a); + a *= 0.5; + cx = cx / (6.0 * a); + cy = cy / (6.0 * a); + //log.warn("props: " + cx + "," + cy + " a=" + a); + + + // quick and dirty minimum spanning circle computation + double d = 0.0; + Point e1 = null; + Point e2 = null; + for (int i = 0; i < tpoly.getVertices().size(); i++) { + Point vi = tpoly.getVertices().get(i); + for (int j = i + 1; j < tpoly.getVertices().size(); j++) { + Point vj = tpoly.getVertices().get(j); + double d1 = vi.getLongitude() - vj.getLongitude(); + double d2 = vi.getLatitude() - vj.getLatitude(); + double dd = Math.sqrt(d1 * d1 + d2 * d2); + if (dd > d) { + d = dd; + e1 = vi; + e2 = vj; + + } + } + } + + PolygonProperties ret = new PolygonProperties(); + ret.windCounterClockwise = (a < 0.0); // RA-DEC increases left-up + //log.warn("a: " + a + " ccw: " + ret.windCounterClockwise); + if (a < 0.0) { + a *= -1.0; + } + ret.area = a; + + CartesianTransform inv = trans.getInverseTransform(); + //log.warn("transform: " + cx + "," + cy + " with " + inv); + ret.center = inv.transform(new Point(cx, cy)); + + // midpoint between vertices + if (e1 != null && e2 != null && d > 0.0) { + Point cen = new Point(0.5 * Math.abs(e1.getLongitude() + e2.getLongitude()), + 0.5 * Math.abs(e1.getLatitude() + e2.getLatitude())); + Point mscc = inv.transform(cen); + ret.minSpanCircle = new Circle(mscc, d / 2.0); + } + + return ret; + } + + /* not needed because dali.Point checks coordinate range + private void validatePoints() throws InvalidPolygonException { + if (points.size() < 3) { + throw new InvalidPolygonException( + "polygon has " + points.size() + " points: minimum 3"); + } + StringBuilder msg = new StringBuilder("invalid Polygon: "); + for (Point p : points) { + boolean flong = p.getLongitude() < 0.0 || p.getLongitude() > 360.0; + boolean flat = p.getLatitude() < -90.0 || p.getLatitude() > 90.0; + if (flong && flat) { + msg.append("longitude,latitude not in [0,360],[-90,90]: ").append(p.getLongitude()).append(",").append(p.getLatitude()); + } else if (flong) { + msg.append("longitude not in [0,360]: ").append(p.getLongitude()); + } else if (flat) { + msg.append("latitude not in [-90,90]: ").append(p.getLatitude()); + } + if (flong || flat) { + throw new InvalidPolygonException(msg.toString()); + } + } + } + */ + + private void validateSegments() throws InvalidPolygonException { + CartesianTransform trans = CartesianTransform.getTransform(this); + Polygon tpoly = trans.transform(this); + + Iterator vi = tpoly.getVertices().iterator(); + List tsegs = new ArrayList<>(); + List psegs = tsegs; + Point start = vi.next(); + Point v1 = start; + while (vi.hasNext()) { + Point v2 = vi.next(); + Segment s = new Segment(v1, v2); + //log.warn("[validateSegments] tseg: " + s); + tsegs.add(s); + v1 = v2; + if (!vi.hasNext()) { + v2 = start; + s = new Segment(v1, v2); + //log.warn("[validateSegments] implicit tseg: " + s); + tsegs.add(s); + } + } + if (this != tpoly) { + // make segments with orig coords for reporting + vi = this.getVertices().iterator(); + psegs = new ArrayList<>(); + start = vi.next(); + v1 = start; + while (vi.hasNext()) { + Point v2 = vi.next(); + Segment s = new Segment(v1, v2); + //log.warn("[validateSegments] pseg: " + s); + psegs.add(s); + v1 = v2; + if (!vi.hasNext()) { + v2 = start; + s = new Segment(v1, v2); + //log.warn("[validateSegments] implicit pseg: " + s); + psegs.add(s); + } + + } + } + intersects(tsegs, psegs); + } + + private static void intersects(List transSegments, List origSegments) throws InvalidPolygonException { + for (int i = 0; i < transSegments.size(); i++) { + Segment s1 = transSegments.get(i); + for (int j = 0; j < transSegments.size(); j++) { + if (i != j) { + Segment s2 = transSegments.get(j); + if (intersects(s1, s2)) { + Segment r1 = origSegments.get(i); + Segment r2 = origSegments.get(j); + throw new InvalidPolygonException("invalid Polygon: segment intersect " + r1 + " vs " + r2); + } + } + } + } + } + + private static boolean intersects(Segment ab, Segment cd) { + //log.debug("intersects: " + ab + " vs " + cd); + // rden = (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) + double den = (ab.v2.getLongitude() - ab.v1.getLongitude()) * (cd.v2.getLatitude() - cd.v1.getLatitude()) + - (ab.v2.getLatitude() - ab.v1.getLatitude()) * (cd.v2.getLongitude() - cd.v1.getLongitude()); + //log.debug("den = " + den); + + //rnum = (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy) + double rnum = (ab.v1.getLatitude() - cd.v1.getLatitude()) * (cd.v2.getLongitude() - cd.v1.getLongitude()) + - (ab.v1.getLongitude() - cd.v1.getLongitude()) * (cd.v2.getLatitude() - cd.v1.getLatitude()); + //log.debug("rnum = " + rnum); + + if (Math.abs(den) < 1.0e-12) { //(den == 0.0) + if (Math.abs(rnum) < 1.0e-12) { //(rnum == 0.0) + // colinear: check overlap on one axis + if (ab.v2 == cd.v1 || ab.v1 == cd.v2) { + return false; // end-to-end + } + double len1 = ab.lengthSquared(); + double len2 = cd.lengthSquared(); + Segment s = ab; + if (len2 > len1) { + s = cd; // the longer one + } + double dx = Math.abs(s.v1.getLongitude() - s.v2.getLongitude()); + double dy = Math.abs(s.v1.getLatitude() - s.v2.getLatitude()); + if (dx > dy) { // more horizontal = project to coordX + if (ab.v2.getLongitude() < cd.v1.getLongitude()) { + return false; // ab left of cd + } + if (ab.v1.getLongitude() > cd.v2.getLongitude()) { + return false; // ab right of cd + } + } else { // more vertical = project to coordY + if (ab.v2.getLatitude() < cd.v1.getLatitude()) { + return false; // ab below cd + } + if (ab.v1.getLatitude() > cd.v2.getLatitude()) { + return false; // ab above cd + } + } + return true; // overlapping + } + return false; // just parallel + } + + double r = rnum / den; + //log.debug("radius = " + radius); + // no intersect, =0 or 1 means the ends touch, which is normal but pg_sphere doesn't like it + //if (radius < 0.0 || radius > 1.0) + if (r <= 0.0 || r >= 1.0) { + return false; + } + + //snum = (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) + double snum = (ab.v1.getLatitude() - cd.v1.getLatitude()) * (ab.v2.getLongitude() - ab.v1.getLongitude()) + - (ab.v1.getLongitude() - cd.v1.getLongitude()) * (ab.v2.getLatitude() - ab.v1.getLatitude()); + //log.debug("snum = " + snum); + + double s = snum / den; + //log.debug("s = " + s); + //if (s < 0.0 || s > 1.0) + if (s <= 0.0 || s >= 1.0) { + return false; // no intersect, =0 or 1 means the ends touch, which is normal + } + + // radius in [0,1] and s in [0,1] = intersects + return true; + } + + private static class Segment { + + Point v1; + Point v2; + + Segment(Point v1, Point v2) { + this.v1 = v1; + this.v2 = v2; + } + + double length() { + return Math.sqrt(lengthSquared()); + } + + double lengthSquared() { + return distanceSquared(v1, v2); + } + + @Override + public String toString() { + return "Segment[" + v1.getLongitude() + "," + v1.getLatitude() + ":" + v2.getLongitude() + "," + v2.getLatitude() + "]"; + } + } + + private static double distanceSquared(Point v1, Point v2) { + return (v1.getLongitude() - v2.getLongitude()) * (v1.getLongitude() - v2.getLongitude()) + + (v1.getLatitude() - v2.getLatitude()) * (v1.getLatitude() - v2.getLatitude()); + } } diff --git a/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java b/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java new file mode 100644 index 00000000..8a27e655 --- /dev/null +++ b/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java @@ -0,0 +1,239 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +************************************************************************ + */ + +package ca.nrc.cadc.dali; + +import ca.nrc.cadc.util.Log4jInit; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.Assert; +import org.junit.Test; + +/** + * Port of CAOM-2.4 polygon validation tests. + * + * @author pdowler + */ +public class PolygonTest { + + private static final Logger log = Logger.getLogger(PolygonTest.class); + + static { + Log4jInit.setLevel("ca.nrc.cadc.dali", Level.INFO); + } + + public PolygonTest() { + } + + @Test + public void testValidPolygon() { + try { + Polygon p = new Polygon(); + p.getVertices().add(new Point(2.0, 2.0)); + p.getVertices().add(new Point(1.0, 4.0)); + p.getVertices().add(new Point(3.0, 3.0)); + + p.validate(); + + Point c = p.getCenter(); + Assert.assertNotNull(c); + Assert.assertEquals(2.0, c.getLongitude(), 0.01); + Assert.assertEquals(3.0, c.getLatitude(), 0.01); + + Assert.assertEquals(1.5, p.getArea(), 0.1); + + Circle msc = p.getMinimumSpanningCircle(); + Assert.assertNotNull(msc); + Assert.assertEquals(1.5, msc.getCenter().getLongitude(), 0.01); + Assert.assertEquals(3.0, msc.getCenter().getLatitude(), 0.01); + Assert.assertEquals(1.12, msc.getRadius(), 0.01); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidPolygonFromFootprintPy() { + // footprint.py output + // old: Polygon ICRS 259.006152 60.047132 259.087308 60.087963 259.089760 60.087730 259.132216 60.068435 259.131770 60.067704 259.133299 60.067895 259.174702 60.049127 259.093400 60.010342 259.078635 60.015734 + String[] oldS = "259.006152 60.047132 259.087308 60.087963 259.089760 60.087730 259.132216 60.068435 259.131770 60.067704 259.133299 60.067895 259.174702 60.049127 259.093400 60.010342 259.078635 60.015734".split(" "); + + // cur: Polygon ICRS 259.006152 60.047132 259.078635 60.015734 259.093400 60.010342 259.174702 60.049127 259.133299 60.067895 259.131770 60.067704 259.132216 60.068435 259.089760 60.087730 259.087308 60.087963 + String[] curS = "259.006152 60.047132 259.078635 60.015734 259.093400 60.010342 259.174702 60.049127 259.133299 60.067895 259.131770 60.067704 259.132216 60.068435 259.089760 60.087730 259.087308 60.087963".split(" "); + + String[] test = oldS; + try { + Polygon p = new Polygon(); + for (int i = 0; i < test.length; i += 2) { + double x = Double.parseDouble(test[i]); + double y = Double.parseDouble(test[i + 1]); + p.getVertices().add(new Point(x, y)); + } + + p.validate(); + + log.info("testValidPolygonFromFootprintPy: " + p); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testInvalidLongitude() { + try { + Polygon p = new Polygon(); + p.getVertices().add(new Point(360.0, 2.0)); + p.getVertices().add(new Point(359.0, 4.0)); + p.getVertices().add(new Point(361.0, 3.0)); + + p.validate(); + Assert.fail("expected IllegalArgumentException, created: " + p); + } catch (IllegalArgumentException expected) { + log.info("caught expected: " + expected); + Assert.assertTrue(expected.getMessage().startsWith("invalid longitude")); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testInvalidLatitude() { + try { + Polygon p = new Polygon(); + p.getVertices().add(new Point(2.0, 89.0)); + p.getVertices().add(new Point(1.0, 91.0)); + p.getVertices().add(new Point(3.0, 90.0)); + + p.validate(); + Assert.fail("expected IllegalArgumentException, created: " + p); + } catch (IllegalArgumentException expected) { + log.info("caught expected: " + expected); + Assert.assertTrue(expected.getMessage().startsWith("invalid latitude")); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testInvalidPolygonCW() { + try { + Polygon p = new Polygon(); + p.getVertices().add(new Point(2.0, 2.0)); + p.getVertices().add(new Point(3.0, 3.0)); + p.getVertices().add(new Point(1.0, 4.0)); + + p.validate(); + Assert.fail("expected InvalidPolygonException, created: " + p); + } catch (InvalidPolygonException expected) { + log.info("caught expected: " + expected); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testInvalidPolygonSegmentIntersect() { + try { + Polygon poly = new Polygon(); + poly.getVertices().add(new Point(2.0, 2.0)); + poly.getVertices().add(new Point(2.0, 4.0)); + poly.getVertices().add(new Point(4.0, 2.0)); + poly.getVertices().add(new Point(4.0, 4.0)); + + try { + poly.validate(); + Assert.fail("expected InvalidPolygonException - got: " + poly); + } catch (InvalidPolygonException expected) { + log.info("testValidateSegments: butterfly " + expected); + Assert.assertTrue(expected.getMessage().startsWith("invalid Polygon: segment intersect ")); + } + + poly.getVertices().clear(); + poly.getVertices().add(new Point(2.0, 2.0)); + poly.getVertices().add(new Point(2.0, 4.0)); + poly.getVertices().add(new Point(5.0, 4.0)); // extra small loop + poly.getVertices().add(new Point(4.0, 5.0)); + poly.getVertices().add(new Point(4.0, 2.0)); + + try { + poly.validate(); + Assert.fail("expected InvalidPolygonException - got: " + poly); + } catch (InvalidPolygonException expected) { + log.info("testValidateSegments: small loop " + expected); + Assert.assertTrue(expected.getMessage().contains("invalid Polygon: segment intersect ")); + } + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } +} From 91d271e285fde0639b9de5e93665f3a37e7eb661 Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Tue, 2 May 2023 16:08:14 -0700 Subject: [PATCH 02/11] add Polygon.getCounterClockwise() --- .../main/java/ca/nrc/cadc/dali/Polygon.java | 18 +++++++++++++++++- .../java/ca/nrc/cadc/dali/PolygonTest.java | 4 ++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java b/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java index ff1ca1f7..988d6dc6 100644 --- a/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java +++ b/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java @@ -75,7 +75,10 @@ import org.apache.log4j.Logger; /** - * DALI polygon class with port of CAOM-2.4 polygon validation code. + * DALI polygon class with port of CAOM-2.4 polygon validation code. Note: the code + * in this class assumes that polygons are small (flat) enough that cartesian math is + * a reasonable approximation and it will give wrong answers for polygons that are too + * large (10s of degrees across??) and especially for those larger than half the sphere. * * @author pdowler */ @@ -130,6 +133,7 @@ public boolean equals(Object obj) { /** * Validate this polygon for conformance to IVOA DALI polygon rules. + * * @throws InvalidPolygonException violation of DALI spec */ public final void validate() throws InvalidPolygonException { @@ -144,6 +148,18 @@ public final void validate() throws InvalidPolygonException { } } + /** + * Check if the polygon is counter-clockwise. + * + * @return true if counter-clockwise (valid), false is clockwise (invalid) + */ + public boolean getCounterClockwise() { + if (ccw == null) { + initProps(); + } + return ccw; + } + public Point getCenter() { if (center == null) { initProps(); diff --git a/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java b/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java index 8a27e655..bde8b8a8 100644 --- a/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java +++ b/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java @@ -99,6 +99,8 @@ public void testValidPolygon() { p.validate(); + Assert.assertTrue("CCW", p.getCounterClockwise()); + Point c = p.getCenter(); Assert.assertNotNull(c); Assert.assertEquals(2.0, c.getLongitude(), 0.01); @@ -189,6 +191,8 @@ public void testInvalidPolygonCW() { p.getVertices().add(new Point(2.0, 2.0)); p.getVertices().add(new Point(3.0, 3.0)); p.getVertices().add(new Point(1.0, 4.0)); + + Assert.assertFalse("CCW", p.getCounterClockwise()); p.validate(); Assert.fail("expected InvalidPolygonException, created: " + p); From b2f87effb8f54e856435531caeb7aa31bde122c0 Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Thu, 28 Sep 2023 23:29:44 -0700 Subject: [PATCH 03/11] sia2: initial SIA-2.0 container service --- sia2/Dockerfile | 3 + sia2/README.md | 75 +++ sia2/VERSION | 6 + sia2/build.gradle | 54 ++ .../org/opencadc/sia2/SiaQuery2ErrorTest.java | 127 ++++ .../org/opencadc/sia2/SiaQuery2GetTest.java | 125 ++++ .../org/opencadc/sia2/VOTableHandler.java | 131 ++++ .../opencadc/sia2/VosiAvailabilityTest.java | 83 +++ .../opencadc/sia2/VosiCapabilitiesTest.java | 83 +++ .../resources/SyncTest-ERROR-BAND.properties | 2 + .../resources/SyncTest-ERROR-CALIB.properties | 2 + .../SyncTest-ERROR-DPTYPE.properties | 2 + .../SyncTest-ERROR-EXPTIME.properties | 2 + .../resources/SyncTest-ERROR-FOV.properties | 2 + .../resources/SyncTest-ERROR-POL.properties | 2 + .../resources/SyncTest-ERROR-POS.properties | 2 + .../SyncTest-ERROR-SPATRES.properties | 2 + .../SyncTest-ERROR-SPECRP.properties | 2 + .../resources/SyncTest-ERROR-TIME.properties | 2 + .../SyncTest-ERROR-TIMERES.properties | 2 + .../SyncTest-OK-BAND-scalar.properties | 3 + .../resources/SyncTest-OK-BAND.properties | 3 + .../resources/SyncTest-OK-CALIB.properties | 4 + .../SyncTest-OK-COLLECTION.properties | 3 + .../resources/SyncTest-OK-DPTYPE.properties | 3 + .../resources/SyncTest-OK-EXPTIME.properties | 3 + .../resources/SyncTest-OK-FACILITY.properties | 3 + .../resources/SyncTest-OK-FORMAT.properties | 3 + .../resources/SyncTest-OK-FOV.properties | 3 + .../resources/SyncTest-OK-ID.properties | 3 + .../SyncTest-OK-INSTRUMENT.properties | 3 + .../resources/SyncTest-OK-POL.properties | 5 + .../SyncTest-OK-POS-Circle.properties | 3 + .../SyncTest-OK-POS-OpenRange.properties | 3 + .../SyncTest-OK-POS-Polygon.properties | 3 + .../SyncTest-OK-POS-Range.properties | 3 + .../resources/SyncTest-OK-SPATRES.properties | 3 + .../resources/SyncTest-OK-SPECRP.properties | 3 + .../resources/SyncTest-OK-TARGET.properties | 3 + .../SyncTest-OK-TIME-scalar.properties | 3 + .../resources/SyncTest-OK-TIME.properties | 3 + .../resources/SyncTest-OK-TIMERES.properties | 3 + .../resources/SyncTest-OK-multiple.properties | 21 + sia2/src/intTest/resources/VOTable-v1.3.xsd | 568 ++++++++++++++++++ ...sable-SyncTest-ERROR-COLLECTION.properties | 2 + ...disable-SyncTest-ERROR-FACILITY.properties | 2 + .../disable-SyncTest-ERROR-FORMAT.properties | 2 + .../disable-SyncTest-ERROR-ID.properties | 2 + ...sable-SyncTest-ERROR-INSTRUMENT.properties | 2 + .../disable-SyncTest-ERROR-TARGET.properties | 2 + .../org/opencadc/sia2/AdqlQueryGenerator.java | 354 +++++++++++ .../org/opencadc/sia2/QueryJobManager.java | 111 ++++ .../opencadc/sia2/ServiceAvailability.java | 219 +++++++ .../java/org/opencadc/sia2/SiaConfig.java | 156 +++++ .../org/opencadc/sia2/SiaParamValidator.java | 170 ++++++ .../java/org/opencadc/sia2/SiaRunner.java | 229 +++++++ .../java/org/opencadc/sia2/UsageError.java | 82 +++ sia2/src/main/webapp/META-INF/context.xml | 18 + sia2/src/main/webapp/WEB-INF/web.xml | 112 ++++ sia2/src/main/webapp/capabilities.xml | 67 +++ sia2/src/main/webapp/index.html | 165 +++++ sia2/src/main/webapp/service.json | 369 ++++++++++++ .../opencadc/sia2/AdqlQueryGeneratorTest.java | 287 +++++++++ .../opencadc/sia2/SiaParamValidatorTest.java | 495 +++++++++++++++ 64 files changed, 4213 insertions(+) create mode 100644 sia2/Dockerfile create mode 100644 sia2/README.md create mode 100644 sia2/VERSION create mode 100644 sia2/build.gradle create mode 100644 sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2ErrorTest.java create mode 100644 sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2GetTest.java create mode 100644 sia2/src/intTest/java/org/opencadc/sia2/VOTableHandler.java create mode 100644 sia2/src/intTest/java/org/opencadc/sia2/VosiAvailabilityTest.java create mode 100644 sia2/src/intTest/java/org/opencadc/sia2/VosiCapabilitiesTest.java create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-BAND.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-CALIB.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-EXPTIME.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-FOV.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-POL.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-POS.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-SPATRES.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-SPECRP.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-TIME.properties create mode 100644 sia2/src/intTest/resources/SyncTest-ERROR-TIMERES.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-BAND-scalar.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-BAND.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-CALIB.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-COLLECTION.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-DPTYPE.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-EXPTIME.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-FACILITY.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-FORMAT.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-FOV.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-ID.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-INSTRUMENT.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-POL.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-POS-Circle.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-POS-OpenRange.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-POS-Polygon.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-POS-Range.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-SPATRES.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-SPECRP.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-TARGET.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-TIME-scalar.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-TIME.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-TIMERES.properties create mode 100644 sia2/src/intTest/resources/SyncTest-OK-multiple.properties create mode 100644 sia2/src/intTest/resources/VOTable-v1.3.xsd create mode 100644 sia2/src/intTest/resources/disable-SyncTest-ERROR-COLLECTION.properties create mode 100644 sia2/src/intTest/resources/disable-SyncTest-ERROR-FACILITY.properties create mode 100644 sia2/src/intTest/resources/disable-SyncTest-ERROR-FORMAT.properties create mode 100644 sia2/src/intTest/resources/disable-SyncTest-ERROR-ID.properties create mode 100644 sia2/src/intTest/resources/disable-SyncTest-ERROR-INSTRUMENT.properties create mode 100644 sia2/src/intTest/resources/disable-SyncTest-ERROR-TARGET.properties create mode 100644 sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java create mode 100644 sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java create mode 100644 sia2/src/main/java/org/opencadc/sia2/ServiceAvailability.java create mode 100644 sia2/src/main/java/org/opencadc/sia2/SiaConfig.java create mode 100644 sia2/src/main/java/org/opencadc/sia2/SiaParamValidator.java create mode 100644 sia2/src/main/java/org/opencadc/sia2/SiaRunner.java create mode 100644 sia2/src/main/java/org/opencadc/sia2/UsageError.java create mode 100644 sia2/src/main/webapp/META-INF/context.xml create mode 100644 sia2/src/main/webapp/WEB-INF/web.xml create mode 100644 sia2/src/main/webapp/capabilities.xml create mode 100644 sia2/src/main/webapp/index.html create mode 100644 sia2/src/main/webapp/service.json create mode 100644 sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java create mode 100644 sia2/src/test/java/org/opencadc/sia2/SiaParamValidatorTest.java diff --git a/sia2/Dockerfile b/sia2/Dockerfile new file mode 100644 index 00000000..8e8876a1 --- /dev/null +++ b/sia2/Dockerfile @@ -0,0 +1,3 @@ +FROM images.opencadc.org/library/cadc-tomcat:1 + +COPY build/libs/sia2.war /usr/share/tomcat/webapps diff --git a/sia2/README.md b/sia2/README.md new file mode 100644 index 00000000..9cd85284 --- /dev/null +++ b/sia2/README.md @@ -0,0 +1,75 @@ +# bifrost + +`sia2` is a [Simple Image Access](https://www.ivoa.net/documents/SIA/) service +that should work with any TAP service that provides an ivoa.ObsCore table (or view). + +## deployment +The `sia2` war file can be renamed at deployment time in order to support an +alternate service name, including introducing additional path elements using the +[war-rename.conf](https://github.com/opencadc/docker-base/tree/master/cadc-tomcat) +feature. + +This service instance is expected to have a PostgreSQL database backend to store UWS +job information. This requirement could be removed in future to support a more lightweight +deployment of what is essentially a facade on a TAP service. + +## configuration +The following configuration files must be available in the `/config` directory. + +### catalina.properties +This file contains java system properties to configure the tomcat server and some of the java +libraries used in the service. + +See cadc-tomcat +for system properties related to the deployment environment. + +See cadc-util +for common system properties. + +`sia2` includes multiple IdentityManager implementations to support authenticated access: + - See cadc-access-control-identity for CADC access-control system support. + - See cadc-gms for OIDC token support. + + `sia2` requires one connection pool to store jobs: +``` +# database connection pools +org.opencadc.sia2.uws.maxActive={max connections for jobs pool} +org.opencadc.sia2.uws.username={database username for jobs pool} +org.opencadc.sia2.uws.password={database password for jobs pool} +org.opencadc.sia2.uws.url=jdbc:postgresql://{server}/{database} +``` +The _uws_ pool manages (create, alter, drop) uws tables and manages the uws content +(creates and modifies jobs in the uws schema when jobs are created and executed by users. + +### cadc-registry.properties +See cadc-registry. + +### sia2.properties +`sia2` must be configured to use a single TAP service to execute queries. +``` +# TAP service +org.opencadc.sia2.queryService = {resourceID or TAP base URL} +``` +The _queryService_ is resolved by a registry lookup and that service is used to query +for CAOM content. It is assumed that this service is deployed "locally" since there can +be many calls and low latency is very desireable. + +`sia2` will attempt to use the caller's identity to query so that CAOM proprietary metadata +protections are enforced, but the details of this depend on the configured IdentityManager +and local A&A service configuration. + +## building it +``` +gradle clean build +docker build -t sia2 -f Dockerfile . +``` + +## checking it +``` +docker run --rm -it sia2:latest /bin/bash +``` + +## running it +``` +docker run --rm --user tomcat:tomcat --volume=/path/to/external/config:/config:ro --name sia2 sia2:latest +``` diff --git a/sia2/VERSION b/sia2/VERSION new file mode 100644 index 00000000..ff0eb44a --- /dev/null +++ b/sia2/VERSION @@ -0,0 +1,6 @@ +## deployable containers have a semantic and build tag +# semantic version tag: major.minor +# build version tag: timestamp +VER=0.1.0 +TAGS="${VER} ${VER}-$(date -u +"%Y%m%dT%H%M%S")" +unset VER diff --git a/sia2/build.gradle b/sia2/build.gradle new file mode 100644 index 00000000..8bf9f0cc --- /dev/null +++ b/sia2/build.gradle @@ -0,0 +1,54 @@ +plugins { + id 'maven' + id 'war' + id 'checkstyle' +} + +repositories { + mavenCentral() + mavenLocal() +} + +apply from: '../opencadc.gradle' + +sourceCompatibility = 1.8 + +group = 'ca.nrc.cadc' + +war { + // Include the swagger-ui so that /sia provides the Sia API documentation + from(System.getenv('RPS') + '/resources/') { + include 'swagger-ui/' + } + from('.') { + include 'VERSION' + } +} + +dependencies { + providedCompile 'javax.servlet:javax.servlet-api:[3.1.0,)' + + compile 'org.opencadc:cadc-util:[1.6.1,)' + compile 'org.opencadc:cadc-cdp:[1.2.3,)' + compile 'org.opencadc:cadc-uws-server:[1.2.4,)' + compile 'org.opencadc:cadc-tap:[1.0,2.0)' + compile 'org.opencadc:cadc-vosi:[1.4.3,2.0)' + + runtime 'org.opencadc:cadc-registry:[1.4.6,)' + runtime 'org.opencadc:cadc-log:[1.0,)' + runtime 'org.opencadc:cadc-gms:[1.0.7,2.0)' + runtime 'org.opencadc:cadc-access-control-identity:[1.1.0,)' + + testCompile 'junit:junit:[4.0,)' + + intTestCompile 'org.opencadc:cadc-test-vosi:[1.0.11,)' + intTestCompile 'org.opencadc:cadc-test-uws:[1.1,)' +} + +configurations { + runtime.exclude group: 'javax.servlet' + runtime.exclude group: 'net.sourceforge.jtds' + runtime.exclude group: 'org.postgresql' + runtime.exclude group: 'org.restlet.jee' +} + diff --git a/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2ErrorTest.java b/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2ErrorTest.java new file mode 100644 index 00000000..6eb4d22a --- /dev/null +++ b/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2ErrorTest.java @@ -0,0 +1,127 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.conformance.uws2.JobResultWrapper; +import ca.nrc.cadc.conformance.uws2.SyncUWSTest; +import ca.nrc.cadc.dali.tables.votable.VOTableDocument; +import ca.nrc.cadc.reg.Standards; +import ca.nrc.cadc.util.FileUtil; +import ca.nrc.cadc.util.Log4jInit; +import java.io.File; +import java.net.URI; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.Assert; + +/** + * + * @author pdowler + */ +public class SiaQuery2ErrorTest extends SyncUWSTest { + + private static final Logger log = Logger.getLogger(SiaQuery2ErrorTest.class); + + static { + Log4jInit.setLevel("ca.nrc.cadc.sia", Level.INFO); + Log4jInit.setLevel("ca.nrc.cadc.conformance.uws2", Level.INFO); + } + + public SiaQuery2ErrorTest() { + super(URI.create("ivo://opencadc.org/sia2"), Standards.SIA_QUERY_20); + + File testFile = FileUtil.getFileFromResource("SyncTest-ERROR-BAND.properties", SiaQuery2ErrorTest.class); + if (testFile.exists()) { + File testDir = testFile.getParentFile(); + super.setPropertiesDir(testDir, "SyncTest-ERROR"); + } + } + + @Override + protected void validateResponse(JobResultWrapper result) { + Assert.assertEquals(400, result.responseCode); + Assert.assertEquals("application/x-votable+xml", result.contentType); + + try { + Assert.assertNotNull(result.syncOutput); + //VOTableDocument vot = VOTableHandler.getVOTable(result.syncOutput); + // because cadc-util HttpTransfer reads the error body and stores it in the Exception + VOTableDocument vot = VOTableHandler.getVOTable(result.throwable); + log.info(result.name + ": found valid VOTable"); + + String queryStatus = VOTableHandler.getQueryStatus(vot); + Assert.assertNotNull("QUERY_STATUS", queryStatus); + Assert.assertEquals("ERROR", queryStatus); + } catch (Exception ex) { + log.error("unexpected exception", ex); + Assert.fail("unexpected exception: " + ex); + } + } +} diff --git a/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2GetTest.java b/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2GetTest.java new file mode 100644 index 00000000..814ecb5d --- /dev/null +++ b/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2GetTest.java @@ -0,0 +1,125 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.conformance.uws2.JobResultWrapper; +import ca.nrc.cadc.conformance.uws2.SyncUWSTest; +import ca.nrc.cadc.dali.tables.votable.VOTableDocument; +import ca.nrc.cadc.reg.Standards; +import ca.nrc.cadc.util.FileUtil; +import ca.nrc.cadc.util.Log4jInit; +import java.io.File; +import java.net.URI; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.Assert; + +/** + * + * @author pdowler + */ +public class SiaQuery2GetTest extends SyncUWSTest { + + private static final Logger log = Logger.getLogger(SiaQuery2GetTest.class); + + static { + Log4jInit.setLevel("ca.nrc.cadc.sia", Level.INFO); + Log4jInit.setLevel("ca.nrc.cadc.conformance.uws2", Level.INFO); + } + + public SiaQuery2GetTest() { + super(URI.create("ivo://opencadc.org/sia2"), Standards.SIA_QUERY_20); + + File testFile = FileUtil.getFileFromResource("SyncTest-OK-BAND.properties", SiaQuery2GetTest.class); + if (testFile.exists()) { + File testDir = testFile.getParentFile(); + super.setPropertiesDir(testDir, "SyncTest-OK"); + } + } + + @Override + protected void validateResponse(JobResultWrapper result) { + Assert.assertEquals(200, result.responseCode); + Assert.assertEquals("application/x-votable+xml", result.contentType); + + try { + Assert.assertNotNull(result.syncOutput); + VOTableDocument vot = VOTableHandler.getVOTable(result.syncOutput); + log.info(result.name + ": found valid VOTable"); + + String queryStatus = VOTableHandler.getQueryStatus(vot); + Assert.assertNotNull("QUERY_STATUS", queryStatus); + Assert.assertEquals("OK", queryStatus); + } catch (Exception ex) { + log.error("unexpected exception", ex); + Assert.fail("unexpected exception: " + ex); + } + } +} diff --git a/sia2/src/intTest/java/org/opencadc/sia2/VOTableHandler.java b/sia2/src/intTest/java/org/opencadc/sia2/VOTableHandler.java new file mode 100644 index 00000000..4cfbc662 --- /dev/null +++ b/sia2/src/intTest/java/org/opencadc/sia2/VOTableHandler.java @@ -0,0 +1,131 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.dali.tables.votable.VOTableDocument; +import ca.nrc.cadc.dali.tables.votable.VOTableInfo; +import ca.nrc.cadc.dali.tables.votable.VOTableReader; +import ca.nrc.cadc.dali.tables.votable.VOTableResource; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.net.URL; +import org.apache.log4j.Logger; +import org.junit.Assert; + +/** + * + * @author pdowler + */ +public abstract class VOTableHandler { + + private static final Logger log = Logger.getLogger(VOTableHandler.class); + + private VOTableHandler() { + } + + static VOTableDocument getVOTable(Reader in) + throws IOException { + VOTableReader vrdr = new VOTableReader(); + return vrdr.read(in); + } + + static VOTableDocument getVOTable(Throwable ex) + throws IOException { + Reader in = new StringReader(ex.getMessage()); + return getVOTable(in); + } + + static VOTableDocument getVOTable(URL url) + throws IOException { + Reader in = new InputStreamReader(url.openStream()); + return getVOTable(in); + } + + static VOTableDocument getVOTable(byte[] ba) + throws IOException { + Reader in = new InputStreamReader(new ByteArrayInputStream(ba)); + return getVOTable(in); + } + + static String getQueryStatus(VOTableDocument vot) { + VOTableResource vr = vot.getResourceByType("results"); + Assert.assertNotNull(vr); + log.debug("found resource: " + vr.getName() + " " + vr.getType()); + for (VOTableInfo vi : vr.getInfos()) { + if ("QUERY_STATUS".equals(vi.getName())) { + return vi.getValue(); + } + } + return null; + } +} diff --git a/sia2/src/intTest/java/org/opencadc/sia2/VosiAvailabilityTest.java b/sia2/src/intTest/java/org/opencadc/sia2/VosiAvailabilityTest.java new file mode 100644 index 00000000..2db5db37 --- /dev/null +++ b/sia2/src/intTest/java/org/opencadc/sia2/VosiAvailabilityTest.java @@ -0,0 +1,83 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.vosi.AvailabilityTest; +import java.net.URI; +import org.apache.log4j.Logger; + +public class VosiAvailabilityTest extends AvailabilityTest { + + private static final Logger log = Logger.getLogger(VosiAvailabilityTest.class); + + public VosiAvailabilityTest() { + super(URI.create("ivo://opencadc.org/sia2")); + } +} diff --git a/sia2/src/intTest/java/org/opencadc/sia2/VosiCapabilitiesTest.java b/sia2/src/intTest/java/org/opencadc/sia2/VosiCapabilitiesTest.java new file mode 100644 index 00000000..851c0b16 --- /dev/null +++ b/sia2/src/intTest/java/org/opencadc/sia2/VosiCapabilitiesTest.java @@ -0,0 +1,83 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.vosi.CapabilitiesTest; +import java.net.URI; +import org.apache.log4j.Logger; + +public class VosiCapabilitiesTest extends CapabilitiesTest { + + private static final Logger log = Logger.getLogger(VosiCapabilitiesTest.class); + + public VosiCapabilitiesTest() { + super(URI.create("ivo://opencadc.org/sia2")); + } +} diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-BAND.properties b/sia2/src/intTest/resources/SyncTest-ERROR-BAND.properties new file mode 100644 index 00000000..3d73d3a6 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-BAND.properties @@ -0,0 +1,2 @@ +BAND=invalid +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-CALIB.properties b/sia2/src/intTest/resources/SyncTest-ERROR-CALIB.properties new file mode 100644 index 00000000..da0cc219 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-CALIB.properties @@ -0,0 +1,2 @@ +CALIB=invalid +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties b/sia2/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties new file mode 100644 index 00000000..cccebf2f --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties @@ -0,0 +1,2 @@ +DPTYPE=invalid +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-EXPTIME.properties b/sia2/src/intTest/resources/SyncTest-ERROR-EXPTIME.properties new file mode 100644 index 00000000..62080f26 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-EXPTIME.properties @@ -0,0 +1,2 @@ +EXPTIME=invalid +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-FOV.properties b/sia2/src/intTest/resources/SyncTest-ERROR-FOV.properties new file mode 100644 index 00000000..6c833bae --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-FOV.properties @@ -0,0 +1,2 @@ +FOV=invalid +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-POL.properties b/sia2/src/intTest/resources/SyncTest-ERROR-POL.properties new file mode 100644 index 00000000..635db9d0 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-POL.properties @@ -0,0 +1,2 @@ +POL=invalid +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-POS.properties b/sia2/src/intTest/resources/SyncTest-ERROR-POS.properties new file mode 100644 index 00000000..d1b417c3 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-POS.properties @@ -0,0 +1,2 @@ +POS=Something bad +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-SPATRES.properties b/sia2/src/intTest/resources/SyncTest-ERROR-SPATRES.properties new file mode 100644 index 00000000..52d900ce --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-SPATRES.properties @@ -0,0 +1,2 @@ +SPATRES=invalid +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-SPECRP.properties b/sia2/src/intTest/resources/SyncTest-ERROR-SPECRP.properties new file mode 100644 index 00000000..051ce0ef --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-SPECRP.properties @@ -0,0 +1,2 @@ +SPECRP=2000 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-TIME.properties b/sia2/src/intTest/resources/SyncTest-ERROR-TIME.properties new file mode 100644 index 00000000..e89970ce --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-TIME.properties @@ -0,0 +1,2 @@ +TIME=invalid +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-TIMERES.properties b/sia2/src/intTest/resources/SyncTest-ERROR-TIMERES.properties new file mode 100644 index 00000000..cff741d5 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-ERROR-TIMERES.properties @@ -0,0 +1,2 @@ +TIMERES=invalid +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-BAND-scalar.properties b/sia2/src/intTest/resources/SyncTest-OK-BAND-scalar.properties new file mode 100644 index 00000000..4869f98e --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-BAND-scalar.properties @@ -0,0 +1,3 @@ +BAND=500e-9 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-BAND.properties b/sia2/src/intTest/resources/SyncTest-OK-BAND.properties new file mode 100644 index 00000000..053a3179 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-BAND.properties @@ -0,0 +1,3 @@ +BAND=500e-9 550e-9 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-CALIB.properties b/sia2/src/intTest/resources/SyncTest-OK-CALIB.properties new file mode 100644 index 00000000..a84d7bef --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-CALIB.properties @@ -0,0 +1,4 @@ +CALIB=2 +CALIB=3 +MAXREC=1 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-COLLECTION.properties b/sia2/src/intTest/resources/SyncTest-OK-COLLECTION.properties new file mode 100644 index 00000000..d6505ab0 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-COLLECTION.properties @@ -0,0 +1,3 @@ +COLLECTION=CFHT +MAXREC=1 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-DPTYPE.properties b/sia2/src/intTest/resources/SyncTest-OK-DPTYPE.properties new file mode 100644 index 00000000..3eb7f31e --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-DPTYPE.properties @@ -0,0 +1,3 @@ +DPTYPE=image +MAXREC=1 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-EXPTIME.properties b/sia2/src/intTest/resources/SyncTest-OK-EXPTIME.properties new file mode 100644 index 00000000..0c5f0f01 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-EXPTIME.properties @@ -0,0 +1,3 @@ +EXPTIME=120.0 240.0 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-FACILITY.properties b/sia2/src/intTest/resources/SyncTest-OK-FACILITY.properties new file mode 100644 index 00000000..85b158f2 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-FACILITY.properties @@ -0,0 +1,3 @@ +FALICITY=CFHT +MAXREC=1 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-FORMAT.properties b/sia2/src/intTest/resources/SyncTest-OK-FORMAT.properties new file mode 100644 index 00000000..ffd13ed3 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-FORMAT.properties @@ -0,0 +1,3 @@ +FORMAT=application/fits +MAXREC=1 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-FOV.properties b/sia2/src/intTest/resources/SyncTest-OK-FOV.properties new file mode 100644 index 00000000..9dfed6d7 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-FOV.properties @@ -0,0 +1,3 @@ +FOV=1.0 2.0 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-ID.properties b/sia2/src/intTest/resources/SyncTest-OK-ID.properties new file mode 100644 index 00000000..4c3de2b4 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-ID.properties @@ -0,0 +1,3 @@ +ID=12345 +MAXREC=1 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-INSTRUMENT.properties b/sia2/src/intTest/resources/SyncTest-OK-INSTRUMENT.properties new file mode 100644 index 00000000..020105eb --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-INSTRUMENT.properties @@ -0,0 +1,3 @@ +INSTRUMENT=MEGAPipe +MAXREC=1 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-POL.properties b/sia2/src/intTest/resources/SyncTest-OK-POL.properties new file mode 100644 index 00000000..f49ee244 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-POL.properties @@ -0,0 +1,5 @@ +POL=I +POL=Q +POL=U +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-POS-Circle.properties b/sia2/src/intTest/resources/SyncTest-OK-POS-Circle.properties new file mode 100644 index 00000000..a33af980 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-POS-Circle.properties @@ -0,0 +1,3 @@ +POS=Circle 12 34 1.0 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-POS-OpenRange.properties b/sia2/src/intTest/resources/SyncTest-OK-POS-OpenRange.properties new file mode 100644 index 00000000..0527a208 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-POS-OpenRange.properties @@ -0,0 +1,3 @@ +POS=Range 10 10 -Inf +Inf +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-POS-Polygon.properties b/sia2/src/intTest/resources/SyncTest-OK-POS-Polygon.properties new file mode 100644 index 00000000..e8d4f0a2 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-POS-Polygon.properties @@ -0,0 +1,3 @@ +POS=Polygon 10 10 12 10 12 12 10 12 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-POS-Range.properties b/sia2/src/intTest/resources/SyncTest-OK-POS-Range.properties new file mode 100644 index 00000000..4fa77cb3 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-POS-Range.properties @@ -0,0 +1,3 @@ +POS=Range 10 10 11 11 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-SPATRES.properties b/sia2/src/intTest/resources/SyncTest-OK-SPATRES.properties new file mode 100644 index 00000000..757ad926 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-SPATRES.properties @@ -0,0 +1,3 @@ +SPATRES=0.05 0.10 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-SPECRP.properties b/sia2/src/intTest/resources/SyncTest-OK-SPECRP.properties new file mode 100644 index 00000000..e6fa30e4 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-SPECRP.properties @@ -0,0 +1,3 @@ +SPECRP=1000 +Inf +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-TARGET.properties b/sia2/src/intTest/resources/SyncTest-OK-TARGET.properties new file mode 100644 index 00000000..e300cd84 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-TARGET.properties @@ -0,0 +1,3 @@ +TARGET=m33 +MAXREC=1 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-TIME-scalar.properties b/sia2/src/intTest/resources/SyncTest-OK-TIME-scalar.properties new file mode 100644 index 00000000..7a11f9d0 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-TIME-scalar.properties @@ -0,0 +1,3 @@ +TIME=54000.0 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-TIME.properties b/sia2/src/intTest/resources/SyncTest-OK-TIME.properties new file mode 100644 index 00000000..e94f0f1c --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-TIME.properties @@ -0,0 +1,3 @@ +TIME=54000.0 54002.0 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/SyncTest-OK-TIMERES.properties b/sia2/src/intTest/resources/SyncTest-OK-TIMERES.properties new file mode 100644 index 00000000..0c9e1922 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-TIMERES.properties @@ -0,0 +1,3 @@ +TIMERES=1.0 2.0 +MAXREC=1 +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/SyncTest-OK-multiple.properties b/sia2/src/intTest/resources/SyncTest-OK-multiple.properties new file mode 100644 index 00000000..e89c0218 --- /dev/null +++ b/sia2/src/intTest/resources/SyncTest-OK-multiple.properties @@ -0,0 +1,21 @@ +BAND=500e-9 550e-9 +CALIB=2 +CALIB=1 +COLLECTION=CFHT +DPTYPE=image +EXPTIME=120.0 240.0 +FACILITY=CFHT +FOV=1.0 2.0 +ID=12345 +INSTRUMENT=MEGAPipe +POL=I +POL=Q +POL=U +POS=Circle 12 34 1.0 +SPATRES=0.05 0.10 +SPECRP=1000 +Inf +TARGET=m33 +TIME=54000.0 54002.0 +TIMERES=1.0 2.0 +MAXREC=1 +expect:Content-Type=application/x-votable+xml diff --git a/sia2/src/intTest/resources/VOTable-v1.3.xsd b/sia2/src/intTest/resources/VOTable-v1.3.xsd new file mode 100644 index 00000000..1d4aaf3e --- /dev/null +++ b/sia2/src/intTest/resources/VOTable-v1.3.xsd @@ -0,0 +1,568 @@ + + + + + VOTable is meant to serialize tabular documents in the + context of Virtual Observatory applications. This schema + corresponds to the VOTable document available from + http://www.ivoa.net/Documents/latest/VOT.html + + + + + + + + + + + + + + + + + + + + Accept UCD1+ + Accept also old UCD1 (but not / + %) including SIAP convention (with :) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + content-role was previsouly restricted as: + + + + + + + + + ]]>; is now a token. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Deprecated in Version 1.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Deprecated in Version 1.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added in Version 1.2: INFO for diagnostics + + + + + + + + + + + + + + + + + + + + + + + + + The 'encoding' attribute is added here to avoid + problems of code generators which do not properly + interpret the TR/TD structures. + 'encoding' was chosen because it appears in + appendix A.5 + + + + + + + + + The ID attribute is added here to the TR tag to avoid + problems of code generators which do not properly + interpret the TR/TD structures + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added in Version 1.2: INFO for diagnostics + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added in Version 1.2: INFO for diagnostics in several places + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-COLLECTION.properties b/sia2/src/intTest/resources/disable-SyncTest-ERROR-COLLECTION.properties new file mode 100644 index 00000000..8710d66a --- /dev/null +++ b/sia2/src/intTest/resources/disable-SyncTest-ERROR-COLLECTION.properties @@ -0,0 +1,2 @@ +COLLECTION=invalid +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-FACILITY.properties b/sia2/src/intTest/resources/disable-SyncTest-ERROR-FACILITY.properties new file mode 100644 index 00000000..b83a012f --- /dev/null +++ b/sia2/src/intTest/resources/disable-SyncTest-ERROR-FACILITY.properties @@ -0,0 +1,2 @@ +FACILITY=invalid +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-FORMAT.properties b/sia2/src/intTest/resources/disable-SyncTest-ERROR-FORMAT.properties new file mode 100644 index 00000000..b18fd5cd --- /dev/null +++ b/sia2/src/intTest/resources/disable-SyncTest-ERROR-FORMAT.properties @@ -0,0 +1,2 @@ +FORMAT=invalid +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-ID.properties b/sia2/src/intTest/resources/disable-SyncTest-ERROR-ID.properties new file mode 100644 index 00000000..c2feaee9 --- /dev/null +++ b/sia2/src/intTest/resources/disable-SyncTest-ERROR-ID.properties @@ -0,0 +1,2 @@ +ID=invalid +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-INSTRUMENT.properties b/sia2/src/intTest/resources/disable-SyncTest-ERROR-INSTRUMENT.properties new file mode 100644 index 00000000..c041ae4b --- /dev/null +++ b/sia2/src/intTest/resources/disable-SyncTest-ERROR-INSTRUMENT.properties @@ -0,0 +1,2 @@ +INSTRUMENT=invalid +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-TARGET.properties b/sia2/src/intTest/resources/disable-SyncTest-ERROR-TARGET.properties new file mode 100644 index 00000000..eb5b3840 --- /dev/null +++ b/sia2/src/intTest/resources/disable-SyncTest-ERROR-TARGET.properties @@ -0,0 +1,2 @@ +TARGET=invalid +expect:Content-Type=application/x-votable+xml \ No newline at end of file diff --git a/sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java b/sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java new file mode 100644 index 00000000..4323d668 --- /dev/null +++ b/sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java @@ -0,0 +1,354 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2019. (c) 2019. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.dali.Circle; +import ca.nrc.cadc.dali.Interval; +import ca.nrc.cadc.dali.Point; +import ca.nrc.cadc.dali.PolarizationState; +import ca.nrc.cadc.dali.Polygon; +import ca.nrc.cadc.dali.Range; +import ca.nrc.cadc.dali.Shape; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; + +/** + * Generate TAP query of the ivoa.ObsCore table from the SIAv2 query parameters. + * + * @author jburke + */ +public class AdqlQueryGenerator { + + private static Logger log = Logger.getLogger(AdqlQueryGenerator.class); + + private Map> queryParams; + + /** + * The input SIA query parameters as structured by the ParamExtractor in cadcDALI. + * + * @param query query input parameters + * @see ca.nrc.cadc.dali.ParamExtractor + */ + public AdqlQueryGenerator(Map> query) { + this.queryParams = query; + } + + /** + * Map with the REQUEST, LANG, and QUERY parameters. + * + * @return map of parameter names and values + */ + public Map getParameterMap() { + Map map = new HashMap(); + map.put("LANG", "ADQL"); + String adql = getQuery(); + log.debug("SIAv2 query:\n" + adql); + map.put("QUERY", adql); + return map; + } + + protected String getQuery() { + StringBuilder query = new StringBuilder(); + query.append("SELECT * FROM ivoa.ObsCore WHERE dataproduct_type IN ( 'image', 'cube' )"); + + SiaParamValidator sia = new SiaParamValidator(); + List pos = sia.validatePOS(queryParams); + if (!pos.isEmpty()) { + boolean needOr = false; + if (pos.size() > 1) { + query.append(" AND ("); + } else { + query.append(" AND "); + } + for (Shape s : pos) { + if (needOr) { + query.append(" OR "); + } + query.append("("); + + query.append("INTERSECTS("); + if (s instanceof Circle) { + Circle c = (Circle) s; + query.append("CIRCLE('ICRS',"); + query.append(c.getCenter().getLongitude()); + query.append(","); + query.append(c.getCenter().getLatitude()); + query.append(","); + query.append(c.getRadius()); + query.append(")"); + } else if (s instanceof Range) { + Range r = (Range) s; + query.append("RANGE_S2D("); + double ralb = 0.0; + double raub = 360.0; + double declb = -90.0; + double decub = 90.0; + if (r.getLongitude().getLower() != null) { + ralb = r.getLongitude().getLower(); + } + if (r.getLongitude().getUpper() != null) { + raub = r.getLongitude().getUpper(); + } + if (r.getLatitude().getLower() != null) { + declb = r.getLatitude().getLower(); + } + if (r.getLatitude().getUpper() != null) { + decub = r.getLatitude().getUpper(); + } + query.append(ralb); + query.append(","); + query.append(raub); + query.append(","); + query.append(declb); + query.append(","); + query.append(decub); + query.append(")"); + } else if (s instanceof Polygon) { + Polygon p = (Polygon) s; + query.append("POLYGON('ICRS',"); + boolean needComma = false; + for (Point v : p.getVertices()) { + if (needComma) { + query.append(","); + } + query.append(v.getLongitude()).append(",").append(v.getLatitude()); + needComma = true; + } + query.append(")"); + } + query.append(", s_region) = 1"); + query.append(")"); + needOr = true; + } + if (pos.size() > 1) { + query.append(")"); + } + } + + List bands = sia.validateBAND(queryParams); + addNumericRangeConstraint(query, "em_min", "em_max", bands); + + List times = sia.validateTIME(queryParams); + addNumericRangeConstraint(query, "t_min", "t_max", times); + + List pols = sia.validatePOL(queryParams); + if (!pols.isEmpty()) { + // for a single pattern-matching LIKE statement, we need to sort the POL values in canoncial order + // and stick in wildcard % whenever there is a gap + // use caom2 PolarizationState for now, possibly copy/move that to an OpenCADC module + //SortedSet polStates = new TreeSet(new PolarizationState.PolStateComparator()); + //for (String p : pols) + //{ + // polStates.add( PolarizationState.valueOf(p)); + //} + + if (pols.size() > 1) { + query.append(" AND ("); + } else { + query.append(" AND "); + } + boolean needOr = false; + for (PolarizationState p : pols) { + if (needOr) { + query.append(" OR "); + } + query.append("("); + query.append("pol_states LIKE '%").append(p.name()).append("%'"); + query.append(")"); + needOr = true; + } + if (pols.size() > 1) { + query.append(")"); + } + } + + List fovs = sia.validateFOV(queryParams); + addNumericRangeConstraint(query, "s_fov", "s_fov", fovs); + + List ress = sia.validateSPATRES(queryParams); + addNumericRangeConstraint(query, "s_resolution", "s_resolution", ress); + + List exptimes = sia.validateEXPTIME(queryParams); + addNumericRangeConstraint(query, "t_exptime", "t_exptime", exptimes); + + List ids = sia.validateID(queryParams); + addStringListConstraint(query, "obs_publisher_did", ids); + + List collections = sia.validateCOLLECTION(queryParams); + addStringListConstraint(query, "obs_collection", collections); + + List facilities = sia.validateFACILITY(queryParams); + addStringListConstraint(query, "facility_name", facilities); + + List instruments = sia.validateINSTRUMENT(queryParams); + addStringListConstraint(query, "instrument_name", instruments); + + List dptypes = sia.validateDPTYPE(queryParams); + addStringListConstraint(query, "dataproduct_type", dptypes); + + List calibs = sia.validateCALIB(queryParams); + addIntegerListConstraint(query, "calib_level", calibs); + + List targets = sia.validateTARGET(queryParams); + addStringListConstraint(query, "target_name", targets); + + List timeress = sia.validateTIMERES(queryParams); + addNumericRangeConstraint(query, "t_resolution", "t_resolution", timeress); + + List specrps = sia.validateSPECRP(queryParams); + addNumericRangeConstraint(query, "em_res_power", "em_res_power", specrps); + + List formats = sia.validateFORMAT(queryParams); + addStringListConstraint(query, "access_format", formats); + + return query.toString(); + } + + private void addNumericRangeConstraint(StringBuilder query, String lbCol, String ubCol, List ranges) { + if (!ranges.isEmpty()) { + if (ranges.size() > 1) { + query.append(" AND ("); + } else { + query.append(" AND "); + } + boolean needOr = false; + for (Interval r : ranges) { + if (needOr) { + query.append(" OR "); + } + query.append("("); + if (lbCol.equals(ubCol) && !Double.isInfinite(r.getLower().doubleValue()) && !Double.isInfinite(r.getUpper().doubleValue())) { + // nicer syntax, better optimised in DB? + query.append(lbCol).append(" BETWEEN ").append(r.getLower()).append(" AND ").append(r.getUpper()); + } else { + if (!Double.isInfinite(r.getUpper().doubleValue())) { + query.append(lbCol).append(" <= ").append(r.getUpper()); + } + if (!Double.isInfinite(r.getLower().doubleValue()) && !Double.isInfinite(r.getUpper().doubleValue())) { + query.append(" AND "); + } + if (!Double.isInfinite(r.getLower().doubleValue())) { + query.append(r.getLower()).append(" <= ").append(ubCol); + } + } + query.append(")"); + needOr = true; + } + if (ranges.size() > 1) { + query.append(")"); + } + } + } + + private void addIntegerListConstraint(StringBuilder query, String column, List values) { + if (!values.isEmpty()) { + query.append(" AND ").append(column); + if (values.size() == 1) { + query.append(" = ").append(values.get(0)); + } else { + query.append(" IN ( "); + boolean first = true; + for (Integer value : values) { + if (first) { + first = false; + } else { + query.append(","); + } + query.append(value); + } + query.append(" )"); + } + } + } + + private void addStringListConstraint(StringBuilder query, String column, List values) { + if (!values.isEmpty()) { + query.append(" AND ").append(column); + if (values.size() == 1) { + query.append(" = '").append(values.get(0)).append("'"); + } else { + query.append(" IN ( "); + boolean first = true; + for (String value : values) { + if (first) { + first = false; + } else { + query.append(","); + } + query.append("'").append(value).append("'"); + } + query.append(" )"); + } + } + } + +} diff --git a/sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java b/sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java new file mode 100644 index 00000000..3f6666cd --- /dev/null +++ b/sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java @@ -0,0 +1,111 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.auth.AuthenticationUtil; +import ca.nrc.cadc.auth.IdentityManager; +import ca.nrc.cadc.uws.server.JobExecutor; +import ca.nrc.cadc.uws.server.JobPersistence; +import ca.nrc.cadc.uws.server.MemoryJobPersistence; +import ca.nrc.cadc.uws.server.RandomStringGenerator; +import ca.nrc.cadc.uws.server.SimpleJobManager; +import ca.nrc.cadc.uws.server.StringIDGenerator; +import ca.nrc.cadc.uws.server.SyncJobExecutor; +import org.apache.log4j.Logger; + +/** + * + * @author pdowler + */ +public class QueryJobManager extends SimpleJobManager { + + private static final Logger log = Logger.getLogger(QueryJobManager.class); + + private static final Long MAX_EXEC_DURATION = 600L; + private static final Long MAX_DESTRUCTION = 7 * 24 * 3600L; // 1 week + private static final Long MAX_QUOTE = 600L; // same as exec since we don't queue + + public QueryJobManager() { + super(); + StringIDGenerator gen = new RandomStringGenerator(16); + IdentityManager im = AuthenticationUtil.getIdentityManager(); + // OK with execOnPOST in web.xml + JobPersistence jobPersist = new MemoryJobPersistence(gen, im, 60L); + + // exec jobs in request thread using custom SiaRunner + JobExecutor jobExec = new SyncJobExecutor(jobPersist, SiaRunner.class); + + super.setJobPersistence(jobPersist); + super.setJobExecutor(jobExec); + super.setMaxExecDuration(MAX_EXEC_DURATION); + super.setMaxDestruction(MAX_DESTRUCTION); + super.setMaxQuote(MAX_QUOTE); + } +} diff --git a/sia2/src/main/java/org/opencadc/sia2/ServiceAvailability.java b/sia2/src/main/java/org/opencadc/sia2/ServiceAvailability.java new file mode 100644 index 00000000..78d7b3ce --- /dev/null +++ b/sia2/src/main/java/org/opencadc/sia2/ServiceAvailability.java @@ -0,0 +1,219 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.auth.AuthMethod; +import ca.nrc.cadc.net.ResourceNotFoundException; +import ca.nrc.cadc.reg.Standards; +import ca.nrc.cadc.reg.client.LocalAuthority; +import ca.nrc.cadc.reg.client.RegistryClient; +import ca.nrc.cadc.vosi.Availability; +import ca.nrc.cadc.vosi.AvailabilityPlugin; +import ca.nrc.cadc.vosi.avail.CheckCertificate; +import ca.nrc.cadc.vosi.avail.CheckDataSource; +import ca.nrc.cadc.vosi.avail.CheckException; +import ca.nrc.cadc.vosi.avail.CheckResource; +import ca.nrc.cadc.vosi.avail.CheckWebService; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.NoSuchElementException; +import org.apache.log4j.Logger; + +/** + * + * @author pdowler + */ +public class ServiceAvailability implements AvailabilityPlugin { + + private static final Logger log = Logger.getLogger(ServiceAvailability.class); + + private static String UWSDS_TEST = "select jobID from uws.Job limit 1"; + + public ServiceAvailability() { + } + + @Override + public void setAppName(String appName) { + //no op + } + + @Override + public boolean heartbeat() { + return true; + } + + public Availability getStatus() { + boolean isGood = true; + String note = "service is accepting queries"; + + try { + CheckResource cr = new CheckDataSource("jdbc/uws", UWSDS_TEST); + cr.check(); + + // TODO: this should be in a library somewhere + //cr = new CheckWcsLib(); + //cr.check(); + // certificate for A&A + File cert = new File(System.getProperty("user.home") + "/.ssl/cadcproxy.pem"); + if (cert.exists()) { + CheckCertificate checkCert = new CheckCertificate(cert); + checkCert.check(); + } + + // check other services we depend on + RegistryClient reg = new RegistryClient(); + URL url; + CheckResource checkResource; + + LocalAuthority localAuthority = new LocalAuthority(); + + try { + URI credURI = localAuthority.getServiceURI(Standards.CRED_PROXY_10.toASCIIString()); + if (credURI != null) { + url = reg.getServiceURL(credURI, Standards.VOSI_AVAILABILITY, AuthMethod.ANON); + if (url != null) { + checkResource = new CheckWebService(url); + checkResource.check(); + } else { + throw new ResourceNotFoundException("registry lookup - not found: " + credURI); + } + } else { + log.debug("not configured: " + Standards.CRED_PROXY_10.toASCIIString()); + } + } catch (NoSuchElementException ex) { // old LocalAuthority behaviour, subject to change + log.debug("not configured: " + Standards.CRED_PROXY_10.toASCIIString()); + } + + URI groupsURI = null; + try { + groupsURI = localAuthority.getServiceURI(Standards.GMS_SEARCH_10.toString()); + if (groupsURI != null) { + url = reg.getServiceURL(groupsURI, Standards.VOSI_AVAILABILITY, AuthMethod.ANON); + if (url != null) { + checkResource = new CheckWebService(url); + checkResource.check(); + } else { + log.warn("registry lookup - not found: " + groupsURI + " does not implement " + Standards.VOSI_AVAILABILITY); + } + } else { + log.debug("not configured: " + Standards.GMS_SEARCH_10.toASCIIString()); + } + } catch (NoSuchElementException ex) { // old LocalAuthority behaviour, subject to change + log.debug("not found: " + Standards.GMS_SEARCH_10.toASCIIString()); + } + + URI usersURI = null; + try { + usersURI = localAuthority.getServiceURI(Standards.UMS_USERS_01.toASCIIString()); + if (usersURI != null) { + if (groupsURI == null || !usersURI.equals(groupsURI)) { + url = reg.getServiceURL(usersURI, Standards.VOSI_AVAILABILITY, AuthMethod.ANON); + if (url != null) { + checkResource = new CheckWebService(url); + checkResource.check(); + } else { + throw new ResourceNotFoundException("registry lookup - not found: " + usersURI + + " " + Standards.VOSI_AVAILABILITY); + } + } else { + log.debug("skipped check because group and user servuices are both " + usersURI); + } + } + } catch (NoSuchElementException ex) { // old LocalAuthority behaviour, subject to change + log.debug("not found: " + Standards.UMS_USERS_01.toASCIIString()); + } + + SiaConfig conf = new SiaConfig(); + url = conf.getAvailailityURL(); + if (url != null) { + checkResource = new CheckWebService(url); + checkResource.check(); + } else { + throw new ResourceNotFoundException("registry lookup - not found: " + conf.getQueryService()); + } + } catch (CheckException ce) { + // tests determined that the resource is not working + isGood = false; + note = ce.getMessage(); + } catch (Throwable t) { + // the test itself failed + log.error("availability test failed", t); + isGood = false; + note = "test failed, reason: " + t; + } + return new Availability(isGood, note); + } + + public void setState(String string) { + //no-op + } +} diff --git a/sia2/src/main/java/org/opencadc/sia2/SiaConfig.java b/sia2/src/main/java/org/opencadc/sia2/SiaConfig.java new file mode 100644 index 00000000..f8f94a2b --- /dev/null +++ b/sia2/src/main/java/org/opencadc/sia2/SiaConfig.java @@ -0,0 +1,156 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +************************************************************************ +*/ + +package org.opencadc.sia2; + +import ca.nrc.cadc.auth.AuthMethod; +import ca.nrc.cadc.net.ResourceNotFoundException; +import ca.nrc.cadc.reg.Standards; +import ca.nrc.cadc.reg.client.RegistryClient; +import ca.nrc.cadc.util.InvalidConfigException; +import ca.nrc.cadc.util.MultiValuedProperties; +import ca.nrc.cadc.util.PropertiesReader; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import org.apache.log4j.Logger; +import org.opencadc.tap.TapClient; + +/** + * + * @author pdowler + */ +public class SiaConfig { + private static final Logger log = Logger.getLogger(SiaConfig.class); + + private static final String CONFIG = "sia2.properties"; + + private static final String BASE_KEY = "org.opencadc.sia2"; + private static final String QUERY_KEY = BASE_KEY + ".queryService"; + + private final URI queryService; + + public SiaConfig() { + StringBuilder sb = new StringBuilder(); + try { + PropertiesReader r = new PropertiesReader(CONFIG); + MultiValuedProperties props = r.getAllProperties(); + + String qs = props.getFirstPropertyValue(QUERY_KEY); + URI qsURI = null; + sb.append("\n\t").append(QUERY_KEY).append(" - "); + if (qs == null) { + sb.append("MISSING"); + } else { + try { + qsURI = new URI(qs); + sb.append("OK"); + } catch (URISyntaxException ex) { + sb.append("ERROR invalid URI: " + qs); + } + } + + if (qsURI == null) { + throw new InvalidConfigException("invalid config: " + sb.toString()); + } + this.queryService = qsURI; + } catch (InvalidConfigException ex) { + throw ex; + } + } + + public URI getQueryService() { + return queryService; + } + + public URL getTapSyncURL() throws MalformedURLException, ResourceNotFoundException { + if (queryService.getScheme().equals("ivo")) { + // registry lookup + RegistryClient reg = new RegistryClient(); + URL base = reg.getServiceURL(queryService, Standards.TAP_10, AuthMethod.ANON); + if (base == null) { + throw new ResourceNotFoundException("not found in registry: " + queryService); + } + return new URL(base.toExternalForm() + "/sync"); + } + + // assume direct URL + return new URL(queryService.toASCIIString() + "/sync"); + } + + public URL getAvailailityURL() throws MalformedURLException { + if (queryService.getScheme().equals("ivo")) { + // registry lookup + RegistryClient reg = new RegistryClient(); + return reg.getServiceURL(queryService, Standards.VOSI_AVAILABILITY, AuthMethod.ANON); + } + + // assume direct URL + return new URL(queryService.toASCIIString() + "/availability"); + } +} diff --git a/sia2/src/main/java/org/opencadc/sia2/SiaParamValidator.java b/sia2/src/main/java/org/opencadc/sia2/SiaParamValidator.java new file mode 100644 index 00000000..f41ecf72 --- /dev/null +++ b/sia2/src/main/java/org/opencadc/sia2/SiaParamValidator.java @@ -0,0 +1,170 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2019. (c) 2019. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.dali.CommonParamValidator; +import ca.nrc.cadc.dali.Interval; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; + +/** + * + * @author pdowler + */ +public class SiaParamValidator extends CommonParamValidator { + + private static final Logger log = Logger.getLogger(SiaParamValidator.class); + + // POS, BAND, TIME, POL, ID params inherited from dali.common.ParamValiator + + // SIA-2.0 params + public static final String FOV = "FOV"; + public static final String SPATRES = "SPATRES"; + public static final String EXPTIME = "EXPTIME"; + public static final String COLLECTION = "COLLECTION"; + public static final String FACILITY = "FACILITY"; + public static final String INSTRUMENT = "INSTRUMENT"; + public static final String DPTYPE = "DPTYPE"; + public static final String CALIB = "CALIB"; + public static final String TARGET = "TARGET"; + public static final String TIMERES = "TIMERES"; + public static final String SPECRP = "SPECRP"; + public static final String FORMAT = "FORMAT"; + + // used by the SiaRunner to pick out supported params only + static final List QUERY_PARAMS = Arrays.asList(POS, BAND, TIME, POL, ID, + FOV, SPATRES, EXPTIME, + COLLECTION, FACILITY, INSTRUMENT, DPTYPE, + CALIB, TARGET, TIMERES, SPECRP, FORMAT); + + // allowed data product types are image and cube + static final List ALLOWED_DPTYPES = Arrays.asList("cube", "image"); + + public SiaParamValidator() { + } + + private String scalar2interval(String s) { + String[] ss = s.split(" "); + if (ss.length == 1) { + return s + " " + s; + } + return s; + } + + public List validateFOV(Map> params) { + return validateNumericInterval(FOV, params); + } + + public List validateSPATRES(Map> params) { + return validateNumericInterval(SPATRES, params); + } + + public List validateEXPTIME(Map> params) { + return validateNumericInterval(EXPTIME, params); + } + + public List validateCOLLECTION(Map> params) { + return validateString(COLLECTION, params, null); + } + + public List validateFACILITY(Map> params) { + return validateString(FACILITY, params, null); + } + + public List validateINSTRUMENT(Map> params) { + return validateString(INSTRUMENT, params, null); + } + + public List validateDPTYPE(Map> params) { + return validateString(DPTYPE, params, ALLOWED_DPTYPES); + } + + public List validateCALIB(Map> params) { + return validateInteger(CALIB, params); + } + + public List validateTARGET(Map> params) { + return validateString(TARGET, params, null); + } + + public List validateTIMERES(Map> params) { + return validateNumericInterval(TIMERES, params); + } + + public List validateSPECRP(Map> params) { + return validateNumericInterval(SPECRP, params); + } + + public List validateFORMAT(Map> params) { + return validateString(FORMAT, params, null); + } +} diff --git a/sia2/src/main/java/org/opencadc/sia2/SiaRunner.java b/sia2/src/main/java/org/opencadc/sia2/SiaRunner.java new file mode 100644 index 00000000..01a5e6a0 --- /dev/null +++ b/sia2/src/main/java/org/opencadc/sia2/SiaRunner.java @@ -0,0 +1,229 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2014. (c) 2014. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.dali.MaxRecValidator; +import ca.nrc.cadc.dali.ParamExtractor; +import ca.nrc.cadc.dali.tables.votable.VOTableWriter; +import ca.nrc.cadc.net.HttpPost; +import ca.nrc.cadc.rest.SyncOutput; +import ca.nrc.cadc.uws.ErrorSummary; +import ca.nrc.cadc.uws.ErrorType; +import ca.nrc.cadc.uws.ExecutionPhase; +import ca.nrc.cadc.uws.Job; +import ca.nrc.cadc.uws.Result; +import ca.nrc.cadc.uws.server.JobRunner; +import ca.nrc.cadc.uws.server.JobUpdater; +import ca.nrc.cadc.uws.util.JobLogInfo; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; + +/** + * Standard JobRunner implementation for SIA-2.0 services. This implementation + * makes the following assumptions: + * + *
    + *
  • hard-coded to generate an ADQL query on the ivoa.ObsCore table + *
  • no support for authenticated calls, use of CDP, etc (TODO) + *
+ * + * @author pdowler + */ +public class SiaRunner implements JobRunner { + + private static Logger log = Logger.getLogger(SiaRunner.class); + + private static final Integer DEF_MAXREC = 1000; + private static final Integer MAX_MAXREC = null; + + private Job job; + private JobUpdater jobUpdater; + private SyncOutput syncOutput; + private JobLogInfo logInfo; + + public void setJob(Job job) { + this.job = job; + } + + public void setJobUpdater(JobUpdater ju) { + jobUpdater = ju; + } + + public void setSyncOutput(SyncOutput so) { + syncOutput = so; + } + + public void run() { + log.debug("RUN SiaRunner: " + job.ownerSubject); + + logInfo = new JobLogInfo(job); + + String startMessage = logInfo.start(); + log.info(startMessage); + + long t1 = System.currentTimeMillis(); + doit(); + long t2 = System.currentTimeMillis(); + + logInfo.setElapsedTime(t2 - t1); + + String endMessage = logInfo.end(); + log.info(endMessage); + } + + private void doit() { + URL url = null; + try { + ExecutionPhase ep = jobUpdater.setPhase(job.getID(), ExecutionPhase.QUEUED, ExecutionPhase.EXECUTING, new Date()); + if (!ExecutionPhase.EXECUTING.equals(ep)) { + String message = job.getID() + ": QUEUED -> EXECUTING [FAILED] -- DONE"; + logInfo.setSuccess(false); + logInfo.setMessage(message); + return; + } + log.debug(job.getID() + ": QUEUED -> EXECUTING [OK]"); + + MaxRecValidator mv = new MaxRecValidator(); + mv.setJob(job); + mv.setDefaultValue(DEF_MAXREC); + mv.setMaxValue(MAX_MAXREC); + Integer maxrec = mv.validate(); + + ParamExtractor pe = new ParamExtractor(SiaParamValidator.QUERY_PARAMS); + Map> queryParams = pe.getParameters(job.getParameterList()); + + // Get the ADQL request parameters. + AdqlQueryGenerator queryGenerator = new AdqlQueryGenerator(queryParams); + Map parameters = queryGenerator.getParameterMap(); + parameters.put("FORMAT", VOTableWriter.CONTENT_TYPE); + if (maxrec != null) { + parameters.put("MAXREC", maxrec); + } + + // the implementation assumes that the /tap/sync service follows the + // POST-redirect-GET (PrG) pattern; cadcUWS SyncServlet does + SiaConfig conf = new SiaConfig(); + URL tapSyncURL = conf.getTapSyncURL(); + + // POST ADQL query to TAP but do not follow redirect to execute it. + HttpPost post = new HttpPost(tapSyncURL, parameters, false); + post.run(); + + // Create an ErrorSummary and throw RuntimeException if the POST failed. + if (post.getThrowable() != null) { + throw new RuntimeException("sync TAP query (" + tapSyncURL.toExternalForm() + + ") failed because " + + post.getThrowable().getMessage()); + } + + // redirect the caller to the G part of the /tap/sync PrG pattern + url = post.getRedirectURL(); + log.debug("redirectURL " + url); + syncOutput.setCode(303); + syncOutput.setHeader("Location", url.toExternalForm()); + + // Mark the Job as completed adding the URL to the query results. + List results = new ArrayList<>(); + results.add(new Result("result", new URI(url.toExternalForm()))); + jobUpdater.setPhase(job.getID(), ExecutionPhase.EXECUTING, ExecutionPhase.COMPLETED, results, new Date()); + } catch (Throwable t) { + logInfo.setSuccess(false); + logInfo.setMessage(t.getMessage()); + log.debug("FAIL", t); + + // temporary hack to convert IllegalArgumentException into UsageError: message + if (t instanceof IllegalArgumentException) { + t = new UsageError(t.getMessage()); + } + try { + VOTableWriter writer = new VOTableWriter(); + syncOutput.setHeader("Content-Type", VOTableWriter.CONTENT_TYPE); + // TODO: chose suitable response code here (assume bad input for now) + syncOutput.setCode(400); + writer.write(t, syncOutput.getOutputStream()); + } catch (IOException ioe) { + log.debug("Error writing error document " + ioe.getMessage()); + } + ErrorSummary errorSummary = new ErrorSummary(t.getMessage(), ErrorType.FATAL, url); + try { + jobUpdater.setPhase(job.getID(), ExecutionPhase.EXECUTING, ExecutionPhase.ERROR, + errorSummary, new Date()); + } catch (Throwable oops) { + log.debug("failed to set final error status after " + t, oops); + } + } + } +} diff --git a/sia2/src/main/java/org/opencadc/sia2/UsageError.java b/sia2/src/main/java/org/opencadc/sia2/UsageError.java new file mode 100644 index 00000000..998ca3c9 --- /dev/null +++ b/sia2/src/main/java/org/opencadc/sia2/UsageError.java @@ -0,0 +1,82 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2014. (c) 2014. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +/** + * + * @author pdowler + */ +public class UsageError extends Throwable { + + public UsageError(String message) { + super(message); + } + +} diff --git a/sia2/src/main/webapp/META-INF/context.xml b/sia2/src/main/webapp/META-INF/context.xml new file mode 100644 index 00000000..701e7941 --- /dev/null +++ b/sia2/src/main/webapp/META-INF/context.xml @@ -0,0 +1,18 @@ + + + + WEB-INF/web.xml + + + + diff --git a/sia2/src/main/webapp/WEB-INF/web.xml b/sia2/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..f03a23f6 --- /dev/null +++ b/sia2/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,112 @@ + + + + + sia2 + + + index.html + + + + 1 + logControl + ca.nrc.cadc.log.LogControlServlet + + logLevel + info + + + logLevelPackages + + oprg.opencadc.sia2 + ca.nrc.cadc.uws + ca.nrc.cadc.rest + ca.nrc.cadc.sia + ca.nrc.cadc.sia2 + ca.nrc.cadc.vosi + + + + + + + 3 + SiaServlet + ca.nrc.cadc.uws.server.JobServlet + + get + ca.nrc.cadc.uws.web.SyncGetAction + + + post + ca.nrc.cadc.uws.web.SyncPostAction + + + ca.nrc.cadc.uws.web.SyncPostAction.execOnPOST + true + + + ca.nrc.cadc.uws.server.JobManager + org.opencadc.sia2.QueryJobManager + + + + + 3 + AvailabilityServlet + ca.nrc.cadc.vosi.AvailabilityServlet + + + + ca.nrc.cadc.vosi.AvailabilityPlugin + org.opencadc.sia2.ServiceAvailability + + + + + 2 + CapabilitiesServlet + ca.nrc.cadc.rest.RestServlet + + init + ca.nrc.cadc.vosi.CapInitAction + + + get + ca.nrc.cadc.vosi.CapGetAction + + + head + ca.nrc.cadc.vosi.CapHeadAction + + + input + /capabilities.xml + + + + + SiaServlet + /query/* + + + + logControl + /logControl/* + + + + AvailabilityServlet + /availability + + + CapabilitiesServlet + /capabilities + + + diff --git a/sia2/src/main/webapp/capabilities.xml b/sia2/src/main/webapp/capabilities.xml new file mode 100644 index 00000000..b2edf260 --- /dev/null +++ b/sia2/src/main/webapp/capabilities.xml @@ -0,0 +1,67 @@ + + + + + + https://replace.me.com/sia/capabilities + + + + + + https://replace.me.com/sia/availability + + + + + + https://replace.me.com/sia/logControl + + + + + + + + + https://replace.me.com/sia/query + + + + + + + collection + Specify the name of a single data collection to search: actual values that can + be used are dependent on the presence of calibrated images in the entire archive; + the values are given in the output as a field with name="collection". + The default is to search all collections (equivalent to collection="ALL"). + + string + + + mode + Specify the type of accessURL to include in the output. Recognised + values are archive (retrieve the full image) and cutout (retrieve approximately + the region-of-interest). The default mode is archive mode. + + string + + + + + + + + https://replace.me.com/sia/v2query + + + + + + + + diff --git a/sia2/src/main/webapp/index.html b/sia2/src/main/webapp/index.html new file mode 100644 index 00000000..88a1e77c --- /dev/null +++ b/sia2/src/main/webapp/index.html @@ -0,0 +1,165 @@ + + + + + SIA-2.0 API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+
+ + diff --git a/sia2/src/main/webapp/service.json b/sia2/src/main/webapp/service.json new file mode 100644 index 00000000..cd6643d9 --- /dev/null +++ b/sia2/src/main/webapp/service.json @@ -0,0 +1,369 @@ +{ + "swagger": "2.0", + "info": { + "version": "2", + "title": "SIA (Simple Image Access) web service", + "description": "IVOA SIA v2" + }, + "schemes": [ + "https" + ], + "basePath": "/sia", + "paths": { + "/query": { + "get": { + "description": "SIA-2.0 query of all collections. |\nThe SIA-2.0 implements all the query parameters described in the latest SIA-2.0 specification: POS, BAND, TIME, POL, FOV, SPATRES, EXPTIME, ID, COLLECTION, FACILITY, INSTRUMENT, DPTYPE, CALIB, TARGET, TIMERES, SPECRP, FORMAT.\n", + "tags": [ + "Simple Image Access 2.0" + ], + "produces": [ + "text/xml" + ], + "responses": { + "200": { + "description": "Successful response", + "schema": { + "$ref": "#/definitions/VOTable" + } + }, + "401": { + "description": "Unauthorized - User not authenticated" + }, + "404": { + "description": "Not Found - User not found" + }, + "500": { + "description": "Internal error" + }, + "503": { + "description": "Service busy" + }, + "default": { + "description": "Unexpeced error", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "parameters": [ + { + "name": "POS", + "in": "query", + "description": "The POS parameter specifies the target coodinates (RA,DEC in degrees, ICRS) to search.", + "required": false, + "type": "string" + }, + { + "name": "MAXREC", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "BAND", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "TIME", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "POL", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "FOV", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "SPATRES", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "EXPTIME", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "ID", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "COLLECTION", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "FACILITY", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "INSTRUMENT", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "DPTYPE", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "CALIB", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "TARGET", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "TIMERES", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "SPECRP", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "FORMAT", + "in": "query", + "description": "", + "required": false, + "type": "string" + } + ] + }, + "post": { + "description": "SIA-2.0 query of all collections. |\nThe SIA-2.0 implements all the query parameters described in the latest SIA-2.0 specification: POS, BAND, TIME, POL, FOV, SPATRES, EXPTIME, ID, COLLECTION, FACILITY, INSTRUMENT, DPTYPE, CALIB, TARGET, TIMERES, SPECRP, FORMAT.\n", + "tags": [ + "Simple Image Access 2.0" + ], + "produces": [ + "text/xml" + ], + "responses": { + "200": { + "description": "Successful response", + "schema": { + "$ref": "#/definitions/VOTable" + } + }, + "401": { + "description": "Unauthorized - User not authenticated" + }, + "404": { + "description": "Not Found - User not found" + }, + "500": { + "description": "Internal error" + }, + "503": { + "description": "Service busy" + }, + "default": { + "description": "Unexpeced error", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "parameters": [ + { + "name": "POS", + "in": "query", + "description": "The POS parameter specifies the target coodinates (RA,DEC in degrees, ICRS) to search.", + "required": false, + "type": "string" + }, + { + "name": "BAND", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "TIME", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "POL", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "FOV", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "SPATRES", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "EXPTIME", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "ID", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "COLLECTION", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "FACILITY", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "INSTRUMENT", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "DPTYPE", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "CALIB", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "TARGET", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "TIMERES", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "SPECRP", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "FORMAT", + "in": "query", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "MAXREC", + "in": "query", + "description": "maximum number of records to return (default: 1000 maximum: no limit)", + "required": false, + "type": "string" + } + ] + } + }, + "/availability": { + "get": { + "tags": [ + "Support Interfaces" + ], + "summary": "VOSI Availability", + "description": "Indicates whether the service is operable and shows the reliability of the service for extended and scheduled requests. If the query parameter 'detail=min' is used, a light weight heart beat test will be performed. The heart beat test returns status 200 if the service is available.", + "parameters": [ + { + "name": "detail", + "in": "query", + "description": "specifies heart beat to be used to check for availability of this service, the value 'min' must be used, otherwise the full availability test will be performed", + "required": false, + "type": "string" + } + ] + } + }, + "/capabilities": { + "get": { + "summary": "VOSI Capabilities", + "tags": [ + "Support Interfaces" + ], + "description": "Provides the service metadata in the form of a list of Capability descriptions. Each of these descriptions is an \nXML element that:\n
    \n
  • states that the service provides a particular, IVOA-standard function;
  • \n
  • lists the interfaces for invoking that function;
  • \n
  • records any details of the implementation of the function that are not defined as default or constant in the standard for that function.
  • \n
\n" + } + } + } +} diff --git a/sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java b/sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java new file mode 100644 index 00000000..ceb8ce5e --- /dev/null +++ b/sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java @@ -0,0 +1,287 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2011. (c) 2011. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.util.CaseInsensitiveStringComparator; +import ca.nrc.cadc.util.Log4jInit; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.Assert; +import org.junit.Test; + +/** + * + * @author pdowler + */ +public class AdqlQueryGeneratorTest { + + private static final Logger log = Logger.getLogger(AdqlQueryGeneratorTest.class); + + static { + Log4jInit.setLevel("ca.nrc.cadc.sia2", Level.INFO); + } + + //@Test + public void testTemplate() { + + try { + + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testNoParams() { + + try { + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + Map tapParams = gen.getParameterMap(); + + String lang = (String) tapParams.get("LANG"); + String adql = (String) tapParams.get("QUERY"); + + Assert.assertEquals("ADQL", lang); + + log.info("testNoParams ADQL:\n" + adql); + Assert.assertTrue("dataproduct_type", adql.contains("dataproduct_type")); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testScalarInterval() { + + try { + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + params.put("BAND", Arrays.asList("550e-9")); + params.put("TIME", Arrays.asList("54321.0")); + + AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + Map tapParams = gen.getParameterMap(); + + String lang = (String) tapParams.get("LANG"); + String adql = (String) tapParams.get("QUERY"); + + Assert.assertEquals("ADQL", lang); + + log.info("testScalarInterval ADQL:\n" + adql); + Assert.assertTrue("dataproduct_type", adql.contains("dataproduct_type")); + Assert.assertTrue("em_min", adql.contains("em_min <=")); + Assert.assertTrue("em_max", adql.contains("<= em_max")); + Assert.assertTrue("t_min", adql.contains("t_min <=")); + Assert.assertTrue("t_max", adql.contains("<= t_max")); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testSingleParams() { + + try { + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + params.put("POS", Arrays.asList("CIRCLE 12.3 45.6 0.2")); + params.put("BAND", Arrays.asList("500e-9 700e-9")); + params.put("TIME", Arrays.asList("54321.0 55432.1")); + params.put("POL", Arrays.asList("I")); + params.put("FOV", Arrays.asList("0.5 +inf")); // > 0.5 deg + params.put("SPATRES", Arrays.asList("-inf 0.2")); // < 0.2 arcsec + params.put("EXPTIME", Arrays.asList("600.0 3600.0")); // 10-60 minutes + + params.put("ID", Arrays.asList("A12345")); + params.put("COLLECTION", Arrays.asList("CFHT")); + params.put("FACILITY", Arrays.asList("JCMT")); + params.put("INSTRUMENT", Arrays.asList("WIRCam")); + params.put("DPTYPE", Arrays.asList("cube")); + params.put("CALIB", Arrays.asList("2")); + params.put("TARGET", Arrays.asList("M33")); + params.put("TIMERES", Arrays.asList("1.0 2.0")); + params.put("SPECRP", Arrays.asList("-inf 500")); + params.put("FORMAT", Arrays.asList("application/fits")); + + AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + String adql = gen.getQuery(); + log.info("testSingleParams ADQL:\n" + adql); + + Assert.assertTrue("dataproduct_type", adql.contains("dataproduct_type")); + + Assert.assertTrue("s_region", adql.contains("s_region")); + Assert.assertTrue("em_min", adql.contains("em_min")); + Assert.assertTrue("em_max", adql.contains("em_max")); + Assert.assertTrue("t_min", adql.contains("t_min")); + Assert.assertTrue("t_max", adql.contains("t_max")); + Assert.assertTrue("pol_states", adql.contains("pol_states")); + + Assert.assertTrue("s_fov", adql.contains("s_fov")); + Assert.assertTrue("s_resolution", adql.contains("s_resolution")); + Assert.assertTrue("t_exptime", adql.contains("t_exptime")); + + Assert.assertTrue("obs_publisher_did", adql.contains("obs_publisher_did")); + Assert.assertTrue("obs_collection", adql.contains("obs_collection")); + Assert.assertTrue("facility_name", adql.contains("facility_name")); + Assert.assertTrue("instrument_name", adql.contains("instrument_name")); + Assert.assertTrue("dataproduct_type", adql.contains("dataproduct_type")); + Assert.assertTrue("calib_level", adql.contains("calib_level")); + Assert.assertTrue("target_name", adql.contains("target_name")); + Assert.assertTrue("t_resolution", adql.contains("t_resolution")); + Assert.assertTrue("em_res_power", adql.contains("em_res_power")); + Assert.assertTrue("access_format", adql.contains("access_format")); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testMultipleParams() { + + try { + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + params.put("POS", Arrays.asList("CIRCLE 12.3 45.6 0.2", "RANGE +20 +22 -10 -8", "POLYGON 10 10 12 10 11 11")); + params.put("BAND", Arrays.asList("500e-9 700e-9", "200e-9 400e-9")); + params.put("TIME", Arrays.asList("54321.0 55432.1", "56789.0 +Inf")); + params.put("POL", Arrays.asList("I", "Q", "U")); + params.put("FOV", Arrays.asList("0.5 +Inf", "-Inf 2.0")); + params.put("SPATRES", Arrays.asList("-Inf 0.2", "0.02 +Inf")); + params.put("EXPTIME", Arrays.asList("10 20", "600.0 3600.0")); + + params.put("ID", Arrays.asList("A12345", "12345B")); + params.put("COLLECTION", Arrays.asList("CFHT", "JCMT")); + params.put("FACILITY", Arrays.asList("JCMT", "BLAST")); + params.put("INSTRUMENT", Arrays.asList("WIRCam", "MEGAPipe")); + params.put("DPTYPE", Arrays.asList("cube", "image")); + params.put("CALIB", Arrays.asList("2", "4")); + params.put("TARGET", Arrays.asList("M33", "LMC")); + params.put("TIMERES", Arrays.asList("1.0 2.0", "-Inf 3.0")); + params.put("SPECRP", Arrays.asList("-Inf 500", "200 300")); + params.put("FORMAT", Arrays.asList("application/fits", "text/xml")); + + AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + String adql = gen.getQuery(); + log.info("testMultipleParams ADQL:\n" + adql); + + Assert.assertTrue("dataproduct_type", adql.contains("dataproduct_type")); + + Assert.assertTrue("s_region", adql.contains("s_region")); + Assert.assertTrue("em_min", adql.contains("em_min")); + Assert.assertTrue("em_max", adql.contains("em_max")); + Assert.assertTrue("t_min", adql.contains("t_min")); + Assert.assertTrue("t_max", adql.contains("t_max")); + Assert.assertTrue("pol_states", adql.contains("pol_states")); + + Assert.assertTrue("s_fov", adql.contains("s_fov")); + Assert.assertTrue("s_resolution", adql.contains("s_resolution")); + Assert.assertTrue("t_exptime", adql.contains("t_exptime")); + + Assert.assertTrue("obs_publisher_did", adql.contains("obs_publisher_did")); + Assert.assertTrue("obs_collection", adql.contains("obs_collection")); + Assert.assertTrue("facility_name", adql.contains("facility_name")); + Assert.assertTrue("instrument_name", adql.contains("instrument_name")); + Assert.assertTrue("dataproduct_type", adql.contains("dataproduct_type")); + Assert.assertTrue("calib_level", adql.contains("calib_level")); + Assert.assertTrue("target_name", adql.contains("target_name")); + Assert.assertTrue("t_resolution", adql.contains("t_resolution")); + Assert.assertTrue("em_res_power", adql.contains("em_res_power")); + Assert.assertTrue("access_format", adql.contains("access_format")); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testCoordRanges() { + + try { + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + params.put("POS", Arrays.asList("RANGE 0 360 -2 2", "RANGE 10 20 -90 90", "RANGE 1 2 3 4")); + + AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + String adql = gen.getQuery(); + log.info("testCoordRanges ADQL:\n" + adql); + + Assert.assertTrue("dataproduct_type", adql.contains("dataproduct_type")); + Assert.assertTrue("s_region", adql.contains("s_region")); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } +} diff --git a/sia2/src/test/java/org/opencadc/sia2/SiaParamValidatorTest.java b/sia2/src/test/java/org/opencadc/sia2/SiaParamValidatorTest.java new file mode 100644 index 00000000..b269f217 --- /dev/null +++ b/sia2/src/test/java/org/opencadc/sia2/SiaParamValidatorTest.java @@ -0,0 +1,495 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2019. (c) 2019. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.sia2; + +import ca.nrc.cadc.dali.DoubleInterval; +import ca.nrc.cadc.util.CaseInsensitiveStringComparator; +import ca.nrc.cadc.util.Log4jInit; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.Assert; +import org.junit.Test; + +/** + * + * @author pdowler + */ +public class SiaParamValidatorTest { + + private static final Logger log = Logger.getLogger(SiaParamValidatorTest.class); + + static { + Log4jInit.setLevel("ca.nrc.cadc.sia2", Level.INFO); + } + + SiaParamValidator sia = new SiaParamValidator(); + + @Test + public void testValidateFOV() { + + String[] testParams = new String[]{"FOV", "fov", "FoV"}; + + try { + List empty = sia.validateFOV(null); // compile and null arg check + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method m = SiaParamValidator.class.getMethod("validateFOV", Map.class); + doValidateNumeric(m, "FOV", testParams); + + // invalid: code is more or less tested already in testValidateBand + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateSPATRES() { + + String[] testParams = new String[]{"SPATRES", "spatres", "SpAtReS"}; + + try { + List empty = sia.validateSPATRES(null); // compile and null arg check + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method m = SiaParamValidator.class.getMethod("validateSPATRES", Map.class); + doValidateNumeric(m, "SPATRES", testParams); + + // invalid: code is more or less tested already in testValidateBand + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateEXPTIME() { + + String[] testParams = new String[]{"EXPTIME", "exptime", "ExPtImE"}; + + try { + List empty = sia.validateEXPTIME(null); // compile and null arg check + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method m = SiaParamValidator.class.getMethod("validateEXPTIME", Map.class); + doValidateNumeric(m, "EXPTIME", testParams); + + // invalid: code is more or less tested already in testValidateBand + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateCOLLECTION() { + try { + String[] testParams = new String[]{"COLLECTION", "collection", "CoLlEcTiOn"}; + + // null arg check + List empty = sia.validateCOLLECTION(null); + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method method = SiaParamValidator.class.getMethod("validateCOLLECTION", Map.class); + doValidateString(method, testParams, null); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateFACILITY() { + try { + String[] testParams = new String[]{"FACILITY", "facility", "FaCiLiTy"}; + + // null arg check + List empty = sia.validateFACILITY(null); + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method method = SiaParamValidator.class.getMethod("validateFACILITY", Map.class); + doValidateString(method, testParams, null); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateINSTRUMENT() { + try { + String[] testParams = new String[]{"INSTRUMENT", "instrument", "InStRuMeNt"}; + + // null arg check + List empty = sia.validateINSTRUMENT(null); + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method method = SiaParamValidator.class.getMethod("validateINSTRUMENT", Map.class); + doValidateString(method, testParams, null); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateDPTYPE() { + try { + String[] testParams = new String[]{"DPTYPE", "dptype", "DpTyPe"}; + String[] testValues = new String[]{"cube", "image"}; + + // null arg check + List empty = sia.validateDPTYPE(null); + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method method = SiaParamValidator.class.getMethod("validateDPTYPE", Map.class); + doValidateString(method, testParams, testValues); + + // test invalid value + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + List vals = new ArrayList(); + vals.add("FOO"); + params.clear(); + params.put(testParams[0], vals); + try { + List ret = sia.validateDPTYPE(params); + Assert.fail("expected IllegalArgumentException,. got: " + ret.size() + " String(s)"); + } catch (IllegalArgumentException expected) { + log.debug("caught expected: " + expected); + } + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateCALIB() { + try { + String[] testParams = new String[]{"CALIB", "calib", "CaLiB"}; + + List empty = sia.validateCALIB(null); // compile and null arg check + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method m = SiaParamValidator.class.getMethod("validateCALIB", Map.class); + doValidateInteger(m, "CALIB", testParams); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateTARGET() { + try { + String[] testParams = new String[]{"TARGET", "target", "TaRgEt"}; + + // null arg check + List empty = sia.validateTARGET(null); + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method method = SiaParamValidator.class.getMethod("validateTARGET", Map.class); + doValidateString(method, testParams, null); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateTIMERES() { + try { + String[] testParams = new String[]{"TIMERES", "timeres", "TiMeReS"}; + + List empty = sia.validateTIMERES(null); // compile and null arg check + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method m = SiaParamValidator.class.getMethod("validateTIMERES", Map.class); + doValidateNumeric(m, "TIMERES", testParams); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateSPECRP() { + try { + String[] testParams = new String[]{"SPECRP", "specrp", "SpEcRp"}; + + List empty = sia.validateSPECRP(null); // compile and null arg check + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method m = SiaParamValidator.class.getMethod("validateSPECRP", Map.class); + doValidateNumeric(m, "SPECRP", testParams); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateFORMAT() { + try { + String[] testParams = new String[]{"FORMAT", "format", "FoRmAt"}; + + // null arg check + List empty = sia.validateFORMAT(null); + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Method method = SiaParamValidator.class.getMethod("validateFORMAT", Map.class); + doValidateString(method, testParams, null); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + @Test + public void testValidateCustomParam() { + try { + List empty = sia.validateString("FOO", null, null); + Assert.assertNotNull(empty); + Assert.assertTrue(empty.isEmpty()); + + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + List vals = new ArrayList(); + vals.add("abc"); + params.put("FOO", vals); + List strs = sia.validateString("FOO", params, null); + Assert.assertNotNull(strs); + Assert.assertEquals(1, strs.size()); + String s = strs.get(0); + Assert.assertEquals("abc", s); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + public void doValidateString(Method testMethod, String[] testParams, String[] testValues) + throws Exception { + if (testValues == null) { + testValues = new String[]{ + "12345", + "ABCDEF", + "abcdef", + "a1b2c3", + "A1b2C3" + }; + } + + int[] len = new int[]{1, testValues.length / 2, testValues.length}; + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + for (String tp : testParams) { + for (int i = 0; i < len.length; i++) { + List vals = new ArrayList(); + for (int j = 0; j < len[i]; j++) { + vals.add(testValues[j]); + } + params.put(tp, vals); + List pols = (List) testMethod.invoke(sia, params); + Assert.assertNotNull(pols); + Assert.assertEquals(len[i], pols.size()); + } + } + } + + public void doValidateInteger(Method m, String paramName, String[] testParams) { + String[] testValues = new String[]{ + "0", + "1", + "2", + "666" + }; + try { + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + + for (String tp : testParams) { + for (String tv : testValues) { + List expected = new ArrayList(); + expected.add(tv); + params.put(tp, expected); + + List actual = (List) m.invoke(sia, params); + + Assert.assertNotNull(actual); + Assert.assertEquals(1, actual.size()); + Integer i = actual.get(0); + Assert.assertEquals(tv, i.toString()); + } + } + + // test multiple values + params.clear(); + List expected = new ArrayList(); + for (String e : testValues) { + expected.add(e); + } + params.put(paramName, expected); + List actual = (List) m.invoke(sia, params); + Assert.assertNotNull(actual); + Assert.assertEquals(expected.size(), actual.size()); + for (String e : expected) { + Integer ei = new Integer(e); + Assert.assertTrue(actual.contains(ei)); + } + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + + public void doValidateNumeric(Method m, String paramName, String[] testParams) { + String LB = "12.3 +Inf"; + String UB = "-Inf 34.5"; + String OPEN = "-Inf +Inf"; + String SCALAR = "1.0 1.0"; + String[] testValues = new String[]{ + "12.3 34.5", + "1.23e1 3.45e1", + "1.23E1 3.45E1", + SCALAR, + LB, + UB, + OPEN + }; + try { + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + + for (String tp : testParams) { + for (String tv : testValues) { + List vals = new ArrayList(); + vals.add(tv); + params.put(tp, vals); + + List ranges = (List) m.invoke(sia, params); + + Assert.assertNotNull(ranges); + Assert.assertEquals(1, ranges.size()); + DoubleInterval r = ranges.get(0); + + if (tv == SCALAR) { + Assert.assertEquals(1.0, r.getLower(), 0.001); + Assert.assertEquals(1.0, r.getUpper(), 0.001); + } else if (tv == LB) { + Assert.assertEquals(12.3, r.getLower(), 0.001); + Assert.assertTrue(r.getUpper().isInfinite()); + } else if (tv == UB) { + Assert.assertTrue(r.getLower().isInfinite()); + Assert.assertEquals(34.5, r.getUpper(), 0.001); + } else if (tv == OPEN) { + Assert.assertTrue(r.getLower().isInfinite()); + Assert.assertTrue(r.getUpper().isInfinite()); + } else { + Assert.assertEquals(12.3, r.getLower(), 0.001); + Assert.assertEquals(34.5, r.getUpper(), 0.001); + } + } + } + + // test multiple values + params.clear(); + List vals = new ArrayList(); + for (int i = 0; i < 3; i++) { + vals.add(testValues[i]); + } + params.put(paramName, vals); + List ranges = (List) m.invoke(sia, params); + Assert.assertNotNull(ranges); + Assert.assertEquals(3, ranges.size()); + for (DoubleInterval r : ranges) { + Assert.assertEquals(12.3, r.getLower(), 0.001); + Assert.assertEquals(34.5, r.getUpper(), 0.001); + } + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } + +} From 6316f386e7a992ed24847f84596c0524a1d9b60b Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Fri, 29 Sep 2023 13:24:00 -0700 Subject: [PATCH 04/11] sia2: enable uws database init --- opencadc.gradle | 43 +++++++- .../org/opencadc/sia2/QueryJobManager.java | 8 +- .../java/org/opencadc/sia2/UWSInitAction.java | 99 +++++++++++++++++++ sia2/src/main/webapp/WEB-INF/web.xml | 10 +- sia2/src/main/webapp/capabilities.xml | 36 +------ 5 files changed, 154 insertions(+), 42 deletions(-) create mode 100644 sia2/src/main/java/org/opencadc/sia2/UWSInitAction.java diff --git a/opencadc.gradle b/opencadc.gradle index e7fb17c5..25e63315 100644 --- a/opencadc.gradle +++ b/opencadc.gradle @@ -1,10 +1,12 @@ configurations { checkstyleDep + intTestCompile.extendsFrom testCompile + intTestRuntime.extendsFrom testRuntime } dependencies { testCompile 'com.puppycrawl.tools:checkstyle:8.2' - checkstyleDep 'org.opencadc:cadc-quality:1.+' + checkstyleDep 'org.opencadc:cadc-quality:[1.0,)' } checkstyle { @@ -14,6 +16,20 @@ checkstyle { sourceSets = [] } +sourceSets { + test { + resources.srcDirs += 'src/test/resources' + } + intTest { + java { + compileClasspath += main.output + test.output + runtimeClasspath += main.output + test.output + } + resources.srcDir file('src/intTest/resources') + resources.srcDirs += new File(System.getenv('A') + '/test-certificates/') + } +} + // Temporary work around for issue https://github.com/gradle/gradle/issues/881 - // gradle not displaying fail build status when warnings reported --> @@ -28,6 +44,25 @@ tasks.withType(Checkstyle).each { checkstyleTask -> } } +tasks.withType(Test) { + // reset the report destinations so that intTests go to their own page + //reports.html.destination = file("${reporting.baseDir}/${name}") + reports.html.destination = file(reporting.baseDir.getAbsolutePath() + '/' + name) + + // Assign all Java system properties from + // the command line to the tests + systemProperties System.properties +} + +task intTest(type: Test) { + // set the configuration context + testClassesDirs = sourceSets.intTest.output.classesDirs + classpath = sourceSets.intTest.runtimeClasspath + + // run the tests always + outputs.upToDateWhen { false } +} + test { testLogging { events "PASSED", "FAILED", "SKIPPED" @@ -35,3 +70,9 @@ test { } } +intTest { + testLogging { + events "PASSED", "FAILED", "SKIPPED" + // "STARTED", + } +} diff --git a/sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java b/sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java index 3f6666cd..3b5225ef 100644 --- a/sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java +++ b/sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java @@ -73,11 +73,9 @@ import ca.nrc.cadc.auth.IdentityManager; import ca.nrc.cadc.uws.server.JobExecutor; import ca.nrc.cadc.uws.server.JobPersistence; -import ca.nrc.cadc.uws.server.MemoryJobPersistence; -import ca.nrc.cadc.uws.server.RandomStringGenerator; import ca.nrc.cadc.uws.server.SimpleJobManager; -import ca.nrc.cadc.uws.server.StringIDGenerator; import ca.nrc.cadc.uws.server.SyncJobExecutor; +import ca.nrc.cadc.uws.server.impl.PostgresJobPersistence; import org.apache.log4j.Logger; /** @@ -94,10 +92,8 @@ public class QueryJobManager extends SimpleJobManager { public QueryJobManager() { super(); - StringIDGenerator gen = new RandomStringGenerator(16); IdentityManager im = AuthenticationUtil.getIdentityManager(); - // OK with execOnPOST in web.xml - JobPersistence jobPersist = new MemoryJobPersistence(gen, im, 60L); + JobPersistence jobPersist = new PostgresJobPersistence(im); // exec jobs in request thread using custom SiaRunner JobExecutor jobExec = new SyncJobExecutor(jobPersist, SiaRunner.class); diff --git a/sia2/src/main/java/org/opencadc/sia2/UWSInitAction.java b/sia2/src/main/java/org/opencadc/sia2/UWSInitAction.java new file mode 100644 index 00000000..c34d2c20 --- /dev/null +++ b/sia2/src/main/java/org/opencadc/sia2/UWSInitAction.java @@ -0,0 +1,99 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2022. (c) 2022. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +************************************************************************ +*/ + +package org.opencadc.sia2; + +import ca.nrc.cadc.db.DBUtil; +import ca.nrc.cadc.rest.InitAction; +import ca.nrc.cadc.uws.server.impl.InitDatabaseUWS; +import javax.sql.DataSource; +import org.apache.log4j.Logger; + +/** + * + * @author pdowler + */ +public class UWSInitAction extends InitAction { + private static final Logger log = Logger.getLogger(UWSInitAction.class); + + public UWSInitAction() { + } + + @Override + public void doInit() { + try { + DataSource uws = DBUtil.findJNDIDataSource("jdbc/uws"); + InitDatabaseUWS uwsi = new InitDatabaseUWS(uws, null, "uws"); + uwsi.doInit(); + log.info("init uws: OK"); + } catch (Exception ex) { + throw new RuntimeException("INIT FAIL", ex); + } + } + + +} diff --git a/sia2/src/main/webapp/WEB-INF/web.xml b/sia2/src/main/webapp/WEB-INF/web.xml index f03a23f6..3b819492 100644 --- a/sia2/src/main/webapp/WEB-INF/web.xml +++ b/sia2/src/main/webapp/WEB-INF/web.xml @@ -23,12 +23,12 @@ logLevelPackages - oprg.opencadc.sia2 + org.opencadc.sia2 ca.nrc.cadc.uws ca.nrc.cadc.rest - ca.nrc.cadc.sia - ca.nrc.cadc.sia2 ca.nrc.cadc.vosi + ca.nrc.cadc.auth + org.opencadc.auth @@ -38,6 +38,10 @@ 3 SiaServlet ca.nrc.cadc.uws.server.JobServlet + + init + org.opencadc.sia2.UWSInitAction + get ca.nrc.cadc.uws.web.SyncGetAction diff --git a/sia2/src/main/webapp/capabilities.xml b/sia2/src/main/webapp/capabilities.xml index b2edf260..b4a5c455 100644 --- a/sia2/src/main/webapp/capabilities.xml +++ b/sia2/src/main/webapp/capabilities.xml @@ -6,57 +6,29 @@ - https://replace.me.com/sia/capabilities + https://replace.me.com/sia2/capabilities - https://replace.me.com/sia/availability + https://replace.me.com/sia2/availability - https://replace.me.com/sia/logControl + https://replace.me.com/sia2/logControl - - - https://replace.me.com/sia/query - - - - - - - collection - Specify the name of a single data collection to search: actual values that can - be used are dependent on the presence of calibrated images in the entire archive; - the values are given in the output as a field with name="collection". - The default is to search all collections (equivalent to collection="ALL"). - - string - - - mode - Specify the type of accessURL to include in the output. Recognised - values are archive (retrieve the full image) and cutout (retrieve approximately - the region-of-interest). The default mode is archive mode. - - string - - - - - https://replace.me.com/sia/v2query + https://replace.me.com/sia2/query From 209010fe6d77743a982f677ce3732392e7b93c48 Mon Sep 17 00:00:00 2001 From: Christine Banek Date: Wed, 25 Oct 2023 15:41:22 -0700 Subject: [PATCH 05/11] Configurable ivoa.ObsCore table name Allows for configuration of the ivoa.ObsCore table name from the properties file. This allows us to configure multiple siav2 services to point at the same database / TAP service, each using a different table name. We have this case at Rubin where each butler dataset has its own ObsCore table. --- .../org/opencadc/sia2/AdqlQueryGenerator.java | 9 ++++-- .../java/org/opencadc/sia2/SiaConfig.java | 13 ++++++++ .../java/org/opencadc/sia2/SiaRunner.java | 4 +-- .../opencadc/sia2/AdqlQueryGeneratorTest.java | 31 ++++++++++++++++--- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java b/sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java index 4323d668..1e0a8664 100644 --- a/sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java +++ b/sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java @@ -90,15 +90,18 @@ public class AdqlQueryGenerator { private static Logger log = Logger.getLogger(AdqlQueryGenerator.class); + private String tableName; private Map> queryParams; /** * The input SIA query parameters as structured by the ParamExtractor in cadcDALI. * * @param query query input parameters + * @param tableName ivoa.ObsCore table name * @see ca.nrc.cadc.dali.ParamExtractor */ - public AdqlQueryGenerator(Map> query) { + public AdqlQueryGenerator(Map> query, String tableName) { + this.tableName = tableName; this.queryParams = query; } @@ -118,7 +121,9 @@ public Map getParameterMap() { protected String getQuery() { StringBuilder query = new StringBuilder(); - query.append("SELECT * FROM ivoa.ObsCore WHERE dataproduct_type IN ( 'image', 'cube' )"); + query.append("SELECT * FROM "); + query.append(tableName); + query.append(" WHERE dataproduct_type IN ( 'image', 'cube' )"); SiaParamValidator sia = new SiaParamValidator(); List pos = sia.validatePOS(queryParams); diff --git a/sia2/src/main/java/org/opencadc/sia2/SiaConfig.java b/sia2/src/main/java/org/opencadc/sia2/SiaConfig.java index f8f94a2b..1837104f 100644 --- a/sia2/src/main/java/org/opencadc/sia2/SiaConfig.java +++ b/sia2/src/main/java/org/opencadc/sia2/SiaConfig.java @@ -92,8 +92,10 @@ public class SiaConfig { private static final String BASE_KEY = "org.opencadc.sia2"; private static final String QUERY_KEY = BASE_KEY + ".queryService"; + private static final String TABLE_KEY = BASE_KEY + ".table"; private final URI queryService; + private final String tableName; public SiaConfig() { StringBuilder sb = new StringBuilder(); @@ -119,11 +121,22 @@ public SiaConfig() { throw new InvalidConfigException("invalid config: " + sb.toString()); } this.queryService = qsURI; + + String tn = props.getFirstPropertyValue(TABLE_KEY); + if (tn == null) { + this.tableName = "ivoa.ObsCore"; + } else { + this.tableName = tn; + } } catch (InvalidConfigException ex) { throw ex; } } + public String getTableName() { + return tableName; + } + public URI getQueryService() { return queryService; } diff --git a/sia2/src/main/java/org/opencadc/sia2/SiaRunner.java b/sia2/src/main/java/org/opencadc/sia2/SiaRunner.java index 01a5e6a0..d5625d8a 100644 --- a/sia2/src/main/java/org/opencadc/sia2/SiaRunner.java +++ b/sia2/src/main/java/org/opencadc/sia2/SiaRunner.java @@ -162,11 +162,12 @@ private void doit() { mv.setMaxValue(MAX_MAXREC); Integer maxrec = mv.validate(); + SiaConfig conf = new SiaConfig(); ParamExtractor pe = new ParamExtractor(SiaParamValidator.QUERY_PARAMS); Map> queryParams = pe.getParameters(job.getParameterList()); // Get the ADQL request parameters. - AdqlQueryGenerator queryGenerator = new AdqlQueryGenerator(queryParams); + AdqlQueryGenerator queryGenerator = new AdqlQueryGenerator(queryParams, conf.getTableName()); Map parameters = queryGenerator.getParameterMap(); parameters.put("FORMAT", VOTableWriter.CONTENT_TYPE); if (maxrec != null) { @@ -175,7 +176,6 @@ private void doit() { // the implementation assumes that the /tap/sync service follows the // POST-redirect-GET (PrG) pattern; cadcUWS SyncServlet does - SiaConfig conf = new SiaConfig(); URL tapSyncURL = conf.getTapSyncURL(); // POST ADQL query to TAP but do not follow redirect to execute it. diff --git a/sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java b/sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java index ceb8ce5e..5c3ac582 100644 --- a/sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java +++ b/sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java @@ -108,7 +108,7 @@ public void testNoParams() { try { Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); Map tapParams = gen.getParameterMap(); String lang = (String) tapParams.get("LANG"); @@ -132,7 +132,7 @@ public void testScalarInterval() { params.put("BAND", Arrays.asList("550e-9")); params.put("TIME", Arrays.asList("54321.0")); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); Map tapParams = gen.getParameterMap(); String lang = (String) tapParams.get("LANG"); @@ -176,7 +176,7 @@ public void testSingleParams() { params.put("SPECRP", Arrays.asList("-inf 500")); params.put("FORMAT", Arrays.asList("application/fits")); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); String adql = gen.getQuery(); log.info("testSingleParams ADQL:\n" + adql); @@ -233,7 +233,7 @@ public void testMultipleParams() { params.put("SPECRP", Arrays.asList("-Inf 500", "200 300")); params.put("FORMAT", Arrays.asList("application/fits", "text/xml")); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); String adql = gen.getQuery(); log.info("testMultipleParams ADQL:\n" + adql); @@ -273,7 +273,7 @@ public void testCoordRanges() { Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); params.put("POS", Arrays.asList("RANGE 0 360 -2 2", "RANGE 10 20 -90 90", "RANGE 1 2 3 4")); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); String adql = gen.getQuery(); log.info("testCoordRanges ADQL:\n" + adql); @@ -284,4 +284,25 @@ public void testCoordRanges() { Assert.fail("unexpected exception: " + unexpected); } } + + @Test + public void testAltTableNames() { + + try { + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); + params.put("POS", Arrays.asList("RANGE 0 360 -2 2", "RANGE 10 20 -90 90", "RANGE 1 2 3 4")); + + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "schema.TableName"); + String adql = gen.getQuery(); + log.info("testCoordRanges ADQL:\n" + adql); + + String selectStmt = "SELECT * FROM schema.TableName WHERE"; + Assert.assertTrue("schema.TableName", adql.contains(selectStmt)); + Assert.assertTrue("dataproduct_type", adql.contains("dataproduct_type")); + Assert.assertTrue("s_region", adql.contains("s_region")); + } catch (Exception unexpected) { + log.error("unexpected exception", unexpected); + Assert.fail("unexpected exception: " + unexpected); + } + } } From a6ed7044c511da4848a9dfbdc85c0a532c7e2986 Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Thu, 2 May 2024 09:55:30 -0700 Subject: [PATCH 06/11] cadc-datalink: update internal enum to match current vocab add static DataLink.Term.getTerm(String) utility method --- cadc-datalink/build.gradle | 2 +- .../java/org/opencadc/datalink/DataLink.java | 37 ++++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/cadc-datalink/build.gradle b/cadc-datalink/build.gradle index 1086e075..016c4156 100644 --- a/cadc-datalink/build.gradle +++ b/cadc-datalink/build.gradle @@ -14,7 +14,7 @@ sourceCompatibility = '1.8' group = 'org.opencadc' -version = '1.1.1' +version = '1.1.2' description = 'OpenCADC DataLink library' def git_url = 'https://github.com/opencadc/dal' diff --git a/cadc-datalink/src/main/java/org/opencadc/datalink/DataLink.java b/cadc-datalink/src/main/java/org/opencadc/datalink/DataLink.java index ba66b05c..50365096 100644 --- a/cadc-datalink/src/main/java/org/opencadc/datalink/DataLink.java +++ b/cadc-datalink/src/main/java/org/opencadc/datalink/DataLink.java @@ -83,31 +83,31 @@ public class DataLink { * Terms from the http://www.ivoa.net/rdf/datalink/core vocabulary */ public enum Term { // TODO: re-use the VocabularyTerm code once extracted from caom2 - THIS("#this"), - PROGENITOR("#progenitor"), - DERIVATION("#derivation"), - DOCUMENTATION("#documentation"), AUXILIARY("#auxiliary"), - WEIGHT("#weight"), - ERROR("#error"), - NOISE("#noise"), - - CALIBRATION("#calibration"), BIAS("#bias"), + CALIBRATION("#calibration"), + CODERIVED("#coderived"), + COUNTERPART("#counterpart"), + CUTOUT("#cutout"), DARK("#dark"), + DERIVATION("#derivation"), + DETEACHED_HEADER("#detached-header"), + DOCUMENTATION("#documentation"), + ERROR("#error"), FLAT("#flat"), - + NOISE("#noise"), + PACKAGE("#package"), PREVIEW("#preview"), PREVIEW_IMAGE("#preview-image"), PREVIEW_PLOT("#preview-plot"), + PROC("#proc"), + PROGENITOR("#progenitor"), + THIS("#this"), THUMBNAIL("#thumbnail"), + WEIGHT("#weight"); - PROC("#proc"), - CUTOUT("#cutout"), - PACKAGE("#package"); - private final String value; private Term(String value) { @@ -117,6 +117,15 @@ private Term(String value) { public String getValue() { return value; } + + public static DataLink.Term getTerm(String s) { + for (Term t : values()) { + if (t.value.equals(s)) { + return t; + } + } + return null; + } } public enum LinkAuthTerm { From 2a25a7d4038ca4efa11b7b2b3e78eff2cfe82385 Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Mon, 17 Jun 2024 11:10:32 -0700 Subject: [PATCH 07/11] dap prototype: implement sia2mode validation rename some classes --- .../main/java/ca/nrc/cadc/dali/Polygon.java | 52 ++------ .../dali/{ => impl}/CartesianTransform.java | 19 ++- dap/Dockerfile | 3 + {sia2 => dap}/README.md | 48 +++++--- {sia2 => dap}/VERSION | 0 {sia2 => dap}/build.gradle | 2 +- .../org/opencadc/dap}/SiaQuery2ErrorTest.java | 2 +- .../org/opencadc/dap}/SiaQuery2GetTest.java | 2 +- .../org/opencadc/dap}/VOTableHandler.java | 2 +- .../opencadc/dap}/VosiAvailabilityTest.java | 2 +- .../opencadc/dap}/VosiCapabilitiesTest.java | 2 +- .../resources/SyncTest-ERROR-BAND.properties | 0 .../resources/SyncTest-ERROR-CALIB.properties | 0 .../SyncTest-ERROR-DPTYPE.properties | 0 .../SyncTest-ERROR-EXPTIME.properties | 0 .../resources/SyncTest-ERROR-FOV.properties | 0 .../resources/SyncTest-ERROR-POL.properties | 0 .../resources/SyncTest-ERROR-POS.properties | 0 .../SyncTest-ERROR-SPATRES.properties | 0 .../SyncTest-ERROR-SPECRP.properties | 0 .../resources/SyncTest-ERROR-TIME.properties | 0 .../SyncTest-ERROR-TIMERES.properties | 0 .../SyncTest-OK-BAND-scalar.properties | 0 .../resources/SyncTest-OK-BAND.properties | 0 .../resources/SyncTest-OK-CALIB.properties | 0 .../SyncTest-OK-COLLECTION.properties | 0 .../resources/SyncTest-OK-DPTYPE.properties | 0 .../resources/SyncTest-OK-EXPTIME.properties | 0 .../resources/SyncTest-OK-FACILITY.properties | 0 .../resources/SyncTest-OK-FORMAT.properties | 0 .../resources/SyncTest-OK-FOV.properties | 0 .../resources/SyncTest-OK-ID.properties | 0 .../SyncTest-OK-INSTRUMENT.properties | 0 .../resources/SyncTest-OK-POL.properties | 0 .../SyncTest-OK-POS-Circle.properties | 0 .../SyncTest-OK-POS-OpenRange.properties | 0 .../SyncTest-OK-POS-Polygon.properties | 0 .../SyncTest-OK-POS-Range.properties | 0 .../resources/SyncTest-OK-SPATRES.properties | 0 .../resources/SyncTest-OK-SPECRP.properties | 0 .../resources/SyncTest-OK-TARGET.properties | 0 .../SyncTest-OK-TIME-scalar.properties | 0 .../resources/SyncTest-OK-TIME.properties | 0 .../resources/SyncTest-OK-TIMERES.properties | 0 .../resources/SyncTest-OK-multiple.properties | 0 .../src/intTest/resources/VOTable-v1.3.xsd | 0 ...sable-SyncTest-ERROR-COLLECTION.properties | 0 ...disable-SyncTest-ERROR-FACILITY.properties | 0 .../disable-SyncTest-ERROR-FORMAT.properties | 0 .../disable-SyncTest-ERROR-ID.properties | 0 ...sable-SyncTest-ERROR-INSTRUMENT.properties | 0 .../disable-SyncTest-ERROR-TARGET.properties | 0 .../org/opencadc/dap}/AdqlQueryGenerator.java | 20 ++-- .../main/java/org/opencadc/dap/DapConfig.java | 45 +++---- .../org/opencadc/dap/DapParamValidator.java | 18 ++- .../java/org/opencadc/dap/DapQueryRunner.java | 26 ++-- .../org/opencadc/dap}/QueryJobManager.java | 4 +- .../opencadc/dap}/ServiceAvailability.java | 4 +- .../java/org/opencadc/dap}/UWSInitAction.java | 2 +- .../java/org/opencadc/dap}/UsageError.java | 2 +- .../src/main/webapp/META-INF/context.xml | 0 {sia2 => dap}/src/main/webapp/WEB-INF/web.xml | 7 +- .../src/main/webapp/capabilities.xml | 8 +- {sia2 => dap}/src/main/webapp/index.html | 0 {sia2 => dap}/src/main/webapp/service.json | 0 .../opencadc/dap}/AdqlQueryGeneratorTest.java | 18 +-- .../opencadc/dap/DapParamValidatorTest.java | 112 ++++++++++-------- sia2/Dockerfile | 3 - 68 files changed, 205 insertions(+), 198 deletions(-) rename cadc-dali/src/main/java/ca/nrc/cadc/dali/{ => impl}/CartesianTransform.java (96%) create mode 100644 dap/Dockerfile rename {sia2 => dap}/README.md (55%) rename {sia2 => dap}/VERSION (100%) rename {sia2 => dap}/build.gradle (97%) rename {sia2/src/intTest/java/org/opencadc/sia2 => dap/src/intTest/java/org/opencadc/dap}/SiaQuery2ErrorTest.java (99%) rename {sia2/src/intTest/java/org/opencadc/sia2 => dap/src/intTest/java/org/opencadc/dap}/SiaQuery2GetTest.java (99%) rename {sia2/src/intTest/java/org/opencadc/sia2 => dap/src/intTest/java/org/opencadc/dap}/VOTableHandler.java (99%) rename {sia2/src/intTest/java/org/opencadc/sia2 => dap/src/intTest/java/org/opencadc/dap}/VosiAvailabilityTest.java (99%) rename {sia2/src/intTest/java/org/opencadc/sia2 => dap/src/intTest/java/org/opencadc/dap}/VosiCapabilitiesTest.java (99%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-BAND.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-CALIB.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-EXPTIME.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-FOV.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-POL.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-POS.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-SPATRES.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-SPECRP.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-TIME.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-ERROR-TIMERES.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-BAND-scalar.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-BAND.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-CALIB.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-COLLECTION.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-DPTYPE.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-EXPTIME.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-FACILITY.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-FORMAT.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-FOV.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-ID.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-INSTRUMENT.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-POL.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-POS-Circle.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-POS-OpenRange.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-POS-Polygon.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-POS-Range.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-SPATRES.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-SPECRP.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-TARGET.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-TIME-scalar.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-TIME.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-TIMERES.properties (100%) rename {sia2 => dap}/src/intTest/resources/SyncTest-OK-multiple.properties (100%) rename {sia2 => dap}/src/intTest/resources/VOTable-v1.3.xsd (100%) rename {sia2 => dap}/src/intTest/resources/disable-SyncTest-ERROR-COLLECTION.properties (100%) rename {sia2 => dap}/src/intTest/resources/disable-SyncTest-ERROR-FACILITY.properties (100%) rename {sia2 => dap}/src/intTest/resources/disable-SyncTest-ERROR-FORMAT.properties (100%) rename {sia2 => dap}/src/intTest/resources/disable-SyncTest-ERROR-ID.properties (100%) rename {sia2 => dap}/src/intTest/resources/disable-SyncTest-ERROR-INSTRUMENT.properties (100%) rename {sia2 => dap}/src/intTest/resources/disable-SyncTest-ERROR-TARGET.properties (100%) rename {sia2/src/main/java/org/opencadc/sia2 => dap/src/main/java/org/opencadc/dap}/AdqlQueryGenerator.java (97%) rename sia2/src/main/java/org/opencadc/sia2/SiaConfig.java => dap/src/main/java/org/opencadc/dap/DapConfig.java (91%) rename sia2/src/main/java/org/opencadc/sia2/SiaParamValidator.java => dap/src/main/java/org/opencadc/dap/DapParamValidator.java (93%) rename sia2/src/main/java/org/opencadc/sia2/SiaRunner.java => dap/src/main/java/org/opencadc/dap/DapQueryRunner.java (93%) rename {sia2/src/main/java/org/opencadc/sia2 => dap/src/main/java/org/opencadc/dap}/QueryJobManager.java (97%) rename {sia2/src/main/java/org/opencadc/sia2 => dap/src/main/java/org/opencadc/dap}/ServiceAvailability.java (99%) rename {sia2/src/main/java/org/opencadc/sia2 => dap/src/main/java/org/opencadc/dap}/UWSInitAction.java (99%) rename {sia2/src/main/java/org/opencadc/sia2 => dap/src/main/java/org/opencadc/dap}/UsageError.java (99%) rename {sia2 => dap}/src/main/webapp/META-INF/context.xml (100%) rename {sia2 => dap}/src/main/webapp/WEB-INF/web.xml (94%) rename {sia2 => dap}/src/main/webapp/capabilities.xml (81%) rename {sia2 => dap}/src/main/webapp/index.html (100%) rename {sia2 => dap}/src/main/webapp/service.json (100%) rename {sia2/src/test/java/org/opencadc/sia2 => dap/src/test/java/org/opencadc/dap}/AdqlQueryGeneratorTest.java (97%) rename sia2/src/test/java/org/opencadc/sia2/SiaParamValidatorTest.java => dap/src/test/java/org/opencadc/dap/DapParamValidatorTest.java (80%) delete mode 100644 sia2/Dockerfile diff --git a/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java b/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java index 988d6dc6..929bea0d 100644 --- a/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java +++ b/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java @@ -3,7 +3,7 @@ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * -* (c) 2023. (c) 2023. +* (c) 2024. (c) 2024. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 @@ -69,6 +69,7 @@ package ca.nrc.cadc.dali; +import ca.nrc.cadc.dali.impl.CartesianTransform; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -85,7 +86,7 @@ public class Polygon implements Shape { private static final Logger log = Logger.getLogger(Polygon.class); - private final List points = new ArrayList(); + private final List vertices = new ArrayList<>(); // lazily computed private transient Point center; @@ -100,7 +101,7 @@ public Polygon() { public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Polygon["); - for (Point v : points) { + for (Point v : vertices) { sb.append(v.getLongitude()).append(" ").append(v.getLatitude()).append(" "); } sb.setCharAt(sb.length() - 1, ']'); @@ -108,7 +109,7 @@ public String toString() { } public List getVertices() { - return points; + return vertices; } @Override @@ -118,12 +119,12 @@ public boolean equals(Object obj) { } Polygon rhs = (Polygon) obj; - if (this.points.size() != rhs.points.size()) { + if (this.vertices.size() != rhs.vertices.size()) { return false; } - for (int i = 0; i < points.size(); i++) { - Point tp = this.points.get(i); - Point rp = rhs.points.get(i); + for (int i = 0; i < vertices.size(); i++) { + Point tp = this.vertices.get(i); + Point rp = rhs.vertices.get(i); if (!tp.equals(rp)) { return false; } @@ -137,10 +138,9 @@ public boolean equals(Object obj) { * @throws InvalidPolygonException violation of DALI spec */ public final void validate() throws InvalidPolygonException { - //validatePoints(); validateSegments(); initProps(); - // DALI polygons are always CCW + // DALI polygons are always "inside on the left" aka CCW // unsupported: if we detect CW here it is equivalent to the region // outside with area = 4*pi - area and larger than half the sphere if (!ccw) { @@ -215,13 +215,11 @@ private PolygonProperties computePolygonProperties() { double a = 0.0; double cx = 0.0; double cy = 0.0; - int lastMoveTo = 0; // start of simple polygon Iterator pi = tpoly.getVertices().iterator(); Point start = pi.next(); Point v1 = start; while (pi.hasNext()) { Point v2 = pi.next(); - //log.warn("props: " + v1 + " " + v2); double tmp = v1.getLongitude() * v2.getLatitude() - v2.getLongitude() * v1.getLatitude(); a += tmp; cx += (v1.getLongitude() + v2.getLongitude()) * tmp; @@ -229,7 +227,6 @@ private PolygonProperties computePolygonProperties() { v1 = v2; if (!pi.hasNext()) { v2 = start; - //log.warn("props/implicit close: " + v1 + " " + v2); tmp = v1.getLongitude() * v2.getLatitude() - v2.getLongitude() * v1.getLatitude(); a += tmp; cx += (v1.getLongitude() + v2.getLongitude()) * tmp; @@ -237,11 +234,10 @@ private PolygonProperties computePolygonProperties() { } } - //log.warn("raw props: " + cx + "," + cy + " a=" + a); a *= 0.5; cx = cx / (6.0 * a); cy = cy / (6.0 * a); - //log.warn("props: " + cx + "," + cy + " a=" + a); + log.debug("props: " + cx + "," + cy + " a=" + a); // quick and dirty minimum spanning circle computation @@ -259,14 +255,12 @@ private PolygonProperties computePolygonProperties() { d = dd; e1 = vi; e2 = vj; - } } } PolygonProperties ret = new PolygonProperties(); ret.windCounterClockwise = (a < 0.0); // RA-DEC increases left-up - //log.warn("a: " + a + " ccw: " + ret.windCounterClockwise); if (a < 0.0) { a *= -1.0; } @@ -287,30 +281,6 @@ private PolygonProperties computePolygonProperties() { return ret; } - /* not needed because dali.Point checks coordinate range - private void validatePoints() throws InvalidPolygonException { - if (points.size() < 3) { - throw new InvalidPolygonException( - "polygon has " + points.size() + " points: minimum 3"); - } - StringBuilder msg = new StringBuilder("invalid Polygon: "); - for (Point p : points) { - boolean flong = p.getLongitude() < 0.0 || p.getLongitude() > 360.0; - boolean flat = p.getLatitude() < -90.0 || p.getLatitude() > 90.0; - if (flong && flat) { - msg.append("longitude,latitude not in [0,360],[-90,90]: ").append(p.getLongitude()).append(",").append(p.getLatitude()); - } else if (flong) { - msg.append("longitude not in [0,360]: ").append(p.getLongitude()); - } else if (flat) { - msg.append("latitude not in [-90,90]: ").append(p.getLatitude()); - } - if (flong || flat) { - throw new InvalidPolygonException(msg.toString()); - } - } - } - */ - private void validateSegments() throws InvalidPolygonException { CartesianTransform trans = CartesianTransform.getTransform(this); Polygon tpoly = trans.transform(this); diff --git a/cadc-dali/src/main/java/ca/nrc/cadc/dali/CartesianTransform.java b/cadc-dali/src/main/java/ca/nrc/cadc/dali/impl/CartesianTransform.java similarity index 96% rename from cadc-dali/src/main/java/ca/nrc/cadc/dali/CartesianTransform.java rename to cadc-dali/src/main/java/ca/nrc/cadc/dali/impl/CartesianTransform.java index 27a908a4..755966dd 100644 --- a/cadc-dali/src/main/java/ca/nrc/cadc/dali/CartesianTransform.java +++ b/cadc-dali/src/main/java/ca/nrc/cadc/dali/impl/CartesianTransform.java @@ -67,12 +67,20 @@ ************************************************************************ */ -package ca.nrc.cadc.dali; +package ca.nrc.cadc.dali.impl; +import ca.nrc.cadc.dali.Circle; +import ca.nrc.cadc.dali.Point; +import ca.nrc.cadc.dali.Polygon; import org.apache.log4j.Logger; -// package access so only usable from Polygon (for now) -class CartesianTransform { +/** + * Utility code to provide a 3D cartesian transform that "rotates" shapes around + * the unit sphere so they are far away from the meridian and poles. + * + * @author pdowler + */ +public class CartesianTransform { private static final Logger log = Logger.getLogger(CartesianTransform.class); // the negative X axis @@ -115,8 +123,7 @@ public static CartesianTransform getTransform(Circle c, boolean force) { return getTransform(cube, force); } - public static CartesianTransform getTransform(double[] cube, - boolean force) { + private static CartesianTransform getTransform(double[] cube, boolean force) { double x1 = cube[0]; double x2 = cube[1]; double y1 = cube[2]; @@ -159,7 +166,7 @@ public static CartesianTransform getTransform(double[] cube, log.debug("straddling meridan at equator: " + trans); return trans; } - return new CartesianTransform(); + return new CartesianTransform(); // no-op } public CartesianTransform getInverseTransform() { diff --git a/dap/Dockerfile b/dap/Dockerfile new file mode 100644 index 00000000..63bdf86f --- /dev/null +++ b/dap/Dockerfile @@ -0,0 +1,3 @@ +FROM images.opencadc.org/library/cadc-tomcat:1 + +COPY build/libs/dap.war /usr/share/tomcat/webapps diff --git a/sia2/README.md b/dap/README.md similarity index 55% rename from sia2/README.md rename to dap/README.md index 9cd85284..e0768ebf 100644 --- a/sia2/README.md +++ b/dap/README.md @@ -1,10 +1,12 @@ -# bifrost +# dap -`sia2` is a [Simple Image Access](https://www.ivoa.net/documents/SIA/) service -that should work with any TAP service that provides an ivoa.ObsCore table (or view). +`dap` is a prototype [Data Access Protocol](https://github.com/ivoa-std/DAP) service +that should work with any TAP service that provides an ivoa.ObsCore table (or view). It +also supports a mode that makes it operate as a compliant +[Simple Image Access 2.0](https://www.ivoa.net/documents/SIA/) service. ## deployment -The `sia2` war file can be renamed at deployment time in order to support an +The `dap` war file can be renamed at deployment time in order to support an alternate service name, including introducing additional path elements using the [war-rename.conf](https://github.com/opencadc/docker-base/tree/master/cadc-tomcat) feature. @@ -26,17 +28,17 @@ for system properties related to the deployment environment. See cadc-util for common system properties. -`sia2` includes multiple IdentityManager implementations to support authenticated access: +`dap` includes multiple IdentityManager implementations to support authenticated access: - See cadc-access-control-identity for CADC access-control system support. - See cadc-gms for OIDC token support. - `sia2` requires one connection pool to store jobs: + `dap` requires one connection pool to store jobs: ``` # database connection pools -org.opencadc.sia2.uws.maxActive={max connections for jobs pool} -org.opencadc.sia2.uws.username={database username for jobs pool} -org.opencadc.sia2.uws.password={database password for jobs pool} -org.opencadc.sia2.uws.url=jdbc:postgresql://{server}/{database} +org.opencadc.dap.uws.maxActive={max connections for jobs pool} +org.opencadc.dap.uws.username={database username for jobs pool} +org.opencadc.dap.uws.password={database password for jobs pool} +org.opencadc.dap.uws.url=jdbc:postgresql://{server}/{database} ``` The _uws_ pool manages (create, alter, drop) uws tables and manages the uws content (creates and modifies jobs in the uws schema when jobs are created and executed by users. @@ -44,32 +46,40 @@ The _uws_ pool manages (create, alter, drop) uws tables and manages the uws cont ### cadc-registry.properties See cadc-registry. -### sia2.properties -`sia2` must be configured to use a single TAP service to execute queries. +### dap.properties +`dap` must be configured to use a single TAP service to execute queries. ``` # TAP service -org.opencadc.sia2.queryService = {resourceID or TAP base URL} +org.opencadc.dap.queryService = {resourceID or TAP base URL} + +# run in backwards compatible SIAv2 mode (optional) +org.opencadc.dap.sia2mode = true | false ``` The _queryService_ is resolved by a registry lookup and that service is used to query for CAOM content. It is assumed that this service is deployed "locally" since there can be many calls and low latency is very desireable. -`sia2` will attempt to use the caller's identity to query so that CAOM proprietary metadata -protections are enforced, but the details of this depend on the configured IdentityManager -and local A&A service configuration. +The _sia2mode_ can be set to make the service behave as an SIA-2.0 service: this causes +the generated query to restrict the ObsCore.dataproduct_type values to `cube` and `image`. +TODO: the `/capabilities` endpoint is currently hard-coded to advertise the `SIA#query-2.0` +standardID so that _sia2mode_ is fully correct; as a result "DAP" mode is not really correct +right now. + +`dap` will attempt to use the caller's identity to query, but the details of this depend +on the configured IdentityManager and local A&A service configuration. ## building it ``` gradle clean build -docker build -t sia2 -f Dockerfile . +docker build -t dap -f Dockerfile . ``` ## checking it ``` -docker run --rm -it sia2:latest /bin/bash +docker run --rm -it dap:latest /bin/bash ``` ## running it ``` -docker run --rm --user tomcat:tomcat --volume=/path/to/external/config:/config:ro --name sia2 sia2:latest +docker run --rm --user tomcat:tomcat --volume=/path/to/external/config:/config:ro --name dap dap:latest ``` diff --git a/sia2/VERSION b/dap/VERSION similarity index 100% rename from sia2/VERSION rename to dap/VERSION diff --git a/sia2/build.gradle b/dap/build.gradle similarity index 97% rename from sia2/build.gradle rename to dap/build.gradle index 8bf9f0cc..c92917f9 100644 --- a/sia2/build.gradle +++ b/dap/build.gradle @@ -11,7 +11,7 @@ repositories { apply from: '../opencadc.gradle' -sourceCompatibility = 1.8 +sourceCompatibility = 11 group = 'ca.nrc.cadc' diff --git a/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2ErrorTest.java b/dap/src/intTest/java/org/opencadc/dap/SiaQuery2ErrorTest.java similarity index 99% rename from sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2ErrorTest.java rename to dap/src/intTest/java/org/opencadc/dap/SiaQuery2ErrorTest.java index 6eb4d22a..08f4f959 100644 --- a/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2ErrorTest.java +++ b/dap/src/intTest/java/org/opencadc/dap/SiaQuery2ErrorTest.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.conformance.uws2.JobResultWrapper; import ca.nrc.cadc.conformance.uws2.SyncUWSTest; diff --git a/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2GetTest.java b/dap/src/intTest/java/org/opencadc/dap/SiaQuery2GetTest.java similarity index 99% rename from sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2GetTest.java rename to dap/src/intTest/java/org/opencadc/dap/SiaQuery2GetTest.java index 814ecb5d..ddb2f2ad 100644 --- a/sia2/src/intTest/java/org/opencadc/sia2/SiaQuery2GetTest.java +++ b/dap/src/intTest/java/org/opencadc/dap/SiaQuery2GetTest.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.conformance.uws2.JobResultWrapper; import ca.nrc.cadc.conformance.uws2.SyncUWSTest; diff --git a/sia2/src/intTest/java/org/opencadc/sia2/VOTableHandler.java b/dap/src/intTest/java/org/opencadc/dap/VOTableHandler.java similarity index 99% rename from sia2/src/intTest/java/org/opencadc/sia2/VOTableHandler.java rename to dap/src/intTest/java/org/opencadc/dap/VOTableHandler.java index 4cfbc662..b5ca230b 100644 --- a/sia2/src/intTest/java/org/opencadc/sia2/VOTableHandler.java +++ b/dap/src/intTest/java/org/opencadc/dap/VOTableHandler.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.dali.tables.votable.VOTableDocument; import ca.nrc.cadc.dali.tables.votable.VOTableInfo; diff --git a/sia2/src/intTest/java/org/opencadc/sia2/VosiAvailabilityTest.java b/dap/src/intTest/java/org/opencadc/dap/VosiAvailabilityTest.java similarity index 99% rename from sia2/src/intTest/java/org/opencadc/sia2/VosiAvailabilityTest.java rename to dap/src/intTest/java/org/opencadc/dap/VosiAvailabilityTest.java index 2db5db37..c03fa7b9 100644 --- a/sia2/src/intTest/java/org/opencadc/sia2/VosiAvailabilityTest.java +++ b/dap/src/intTest/java/org/opencadc/dap/VosiAvailabilityTest.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.vosi.AvailabilityTest; import java.net.URI; diff --git a/sia2/src/intTest/java/org/opencadc/sia2/VosiCapabilitiesTest.java b/dap/src/intTest/java/org/opencadc/dap/VosiCapabilitiesTest.java similarity index 99% rename from sia2/src/intTest/java/org/opencadc/sia2/VosiCapabilitiesTest.java rename to dap/src/intTest/java/org/opencadc/dap/VosiCapabilitiesTest.java index 851c0b16..f1f8ce11 100644 --- a/sia2/src/intTest/java/org/opencadc/sia2/VosiCapabilitiesTest.java +++ b/dap/src/intTest/java/org/opencadc/dap/VosiCapabilitiesTest.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.vosi.CapabilitiesTest; import java.net.URI; diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-BAND.properties b/dap/src/intTest/resources/SyncTest-ERROR-BAND.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-BAND.properties rename to dap/src/intTest/resources/SyncTest-ERROR-BAND.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-CALIB.properties b/dap/src/intTest/resources/SyncTest-ERROR-CALIB.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-CALIB.properties rename to dap/src/intTest/resources/SyncTest-ERROR-CALIB.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties b/dap/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties rename to dap/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-EXPTIME.properties b/dap/src/intTest/resources/SyncTest-ERROR-EXPTIME.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-EXPTIME.properties rename to dap/src/intTest/resources/SyncTest-ERROR-EXPTIME.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-FOV.properties b/dap/src/intTest/resources/SyncTest-ERROR-FOV.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-FOV.properties rename to dap/src/intTest/resources/SyncTest-ERROR-FOV.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-POL.properties b/dap/src/intTest/resources/SyncTest-ERROR-POL.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-POL.properties rename to dap/src/intTest/resources/SyncTest-ERROR-POL.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-POS.properties b/dap/src/intTest/resources/SyncTest-ERROR-POS.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-POS.properties rename to dap/src/intTest/resources/SyncTest-ERROR-POS.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-SPATRES.properties b/dap/src/intTest/resources/SyncTest-ERROR-SPATRES.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-SPATRES.properties rename to dap/src/intTest/resources/SyncTest-ERROR-SPATRES.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-SPECRP.properties b/dap/src/intTest/resources/SyncTest-ERROR-SPECRP.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-SPECRP.properties rename to dap/src/intTest/resources/SyncTest-ERROR-SPECRP.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-TIME.properties b/dap/src/intTest/resources/SyncTest-ERROR-TIME.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-TIME.properties rename to dap/src/intTest/resources/SyncTest-ERROR-TIME.properties diff --git a/sia2/src/intTest/resources/SyncTest-ERROR-TIMERES.properties b/dap/src/intTest/resources/SyncTest-ERROR-TIMERES.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-ERROR-TIMERES.properties rename to dap/src/intTest/resources/SyncTest-ERROR-TIMERES.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-BAND-scalar.properties b/dap/src/intTest/resources/SyncTest-OK-BAND-scalar.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-BAND-scalar.properties rename to dap/src/intTest/resources/SyncTest-OK-BAND-scalar.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-BAND.properties b/dap/src/intTest/resources/SyncTest-OK-BAND.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-BAND.properties rename to dap/src/intTest/resources/SyncTest-OK-BAND.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-CALIB.properties b/dap/src/intTest/resources/SyncTest-OK-CALIB.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-CALIB.properties rename to dap/src/intTest/resources/SyncTest-OK-CALIB.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-COLLECTION.properties b/dap/src/intTest/resources/SyncTest-OK-COLLECTION.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-COLLECTION.properties rename to dap/src/intTest/resources/SyncTest-OK-COLLECTION.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-DPTYPE.properties b/dap/src/intTest/resources/SyncTest-OK-DPTYPE.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-DPTYPE.properties rename to dap/src/intTest/resources/SyncTest-OK-DPTYPE.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-EXPTIME.properties b/dap/src/intTest/resources/SyncTest-OK-EXPTIME.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-EXPTIME.properties rename to dap/src/intTest/resources/SyncTest-OK-EXPTIME.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-FACILITY.properties b/dap/src/intTest/resources/SyncTest-OK-FACILITY.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-FACILITY.properties rename to dap/src/intTest/resources/SyncTest-OK-FACILITY.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-FORMAT.properties b/dap/src/intTest/resources/SyncTest-OK-FORMAT.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-FORMAT.properties rename to dap/src/intTest/resources/SyncTest-OK-FORMAT.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-FOV.properties b/dap/src/intTest/resources/SyncTest-OK-FOV.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-FOV.properties rename to dap/src/intTest/resources/SyncTest-OK-FOV.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-ID.properties b/dap/src/intTest/resources/SyncTest-OK-ID.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-ID.properties rename to dap/src/intTest/resources/SyncTest-OK-ID.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-INSTRUMENT.properties b/dap/src/intTest/resources/SyncTest-OK-INSTRUMENT.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-INSTRUMENT.properties rename to dap/src/intTest/resources/SyncTest-OK-INSTRUMENT.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-POL.properties b/dap/src/intTest/resources/SyncTest-OK-POL.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-POL.properties rename to dap/src/intTest/resources/SyncTest-OK-POL.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-POS-Circle.properties b/dap/src/intTest/resources/SyncTest-OK-POS-Circle.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-POS-Circle.properties rename to dap/src/intTest/resources/SyncTest-OK-POS-Circle.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-POS-OpenRange.properties b/dap/src/intTest/resources/SyncTest-OK-POS-OpenRange.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-POS-OpenRange.properties rename to dap/src/intTest/resources/SyncTest-OK-POS-OpenRange.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-POS-Polygon.properties b/dap/src/intTest/resources/SyncTest-OK-POS-Polygon.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-POS-Polygon.properties rename to dap/src/intTest/resources/SyncTest-OK-POS-Polygon.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-POS-Range.properties b/dap/src/intTest/resources/SyncTest-OK-POS-Range.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-POS-Range.properties rename to dap/src/intTest/resources/SyncTest-OK-POS-Range.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-SPATRES.properties b/dap/src/intTest/resources/SyncTest-OK-SPATRES.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-SPATRES.properties rename to dap/src/intTest/resources/SyncTest-OK-SPATRES.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-SPECRP.properties b/dap/src/intTest/resources/SyncTest-OK-SPECRP.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-SPECRP.properties rename to dap/src/intTest/resources/SyncTest-OK-SPECRP.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-TARGET.properties b/dap/src/intTest/resources/SyncTest-OK-TARGET.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-TARGET.properties rename to dap/src/intTest/resources/SyncTest-OK-TARGET.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-TIME-scalar.properties b/dap/src/intTest/resources/SyncTest-OK-TIME-scalar.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-TIME-scalar.properties rename to dap/src/intTest/resources/SyncTest-OK-TIME-scalar.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-TIME.properties b/dap/src/intTest/resources/SyncTest-OK-TIME.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-TIME.properties rename to dap/src/intTest/resources/SyncTest-OK-TIME.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-TIMERES.properties b/dap/src/intTest/resources/SyncTest-OK-TIMERES.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-TIMERES.properties rename to dap/src/intTest/resources/SyncTest-OK-TIMERES.properties diff --git a/sia2/src/intTest/resources/SyncTest-OK-multiple.properties b/dap/src/intTest/resources/SyncTest-OK-multiple.properties similarity index 100% rename from sia2/src/intTest/resources/SyncTest-OK-multiple.properties rename to dap/src/intTest/resources/SyncTest-OK-multiple.properties diff --git a/sia2/src/intTest/resources/VOTable-v1.3.xsd b/dap/src/intTest/resources/VOTable-v1.3.xsd similarity index 100% rename from sia2/src/intTest/resources/VOTable-v1.3.xsd rename to dap/src/intTest/resources/VOTable-v1.3.xsd diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-COLLECTION.properties b/dap/src/intTest/resources/disable-SyncTest-ERROR-COLLECTION.properties similarity index 100% rename from sia2/src/intTest/resources/disable-SyncTest-ERROR-COLLECTION.properties rename to dap/src/intTest/resources/disable-SyncTest-ERROR-COLLECTION.properties diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-FACILITY.properties b/dap/src/intTest/resources/disable-SyncTest-ERROR-FACILITY.properties similarity index 100% rename from sia2/src/intTest/resources/disable-SyncTest-ERROR-FACILITY.properties rename to dap/src/intTest/resources/disable-SyncTest-ERROR-FACILITY.properties diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-FORMAT.properties b/dap/src/intTest/resources/disable-SyncTest-ERROR-FORMAT.properties similarity index 100% rename from sia2/src/intTest/resources/disable-SyncTest-ERROR-FORMAT.properties rename to dap/src/intTest/resources/disable-SyncTest-ERROR-FORMAT.properties diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-ID.properties b/dap/src/intTest/resources/disable-SyncTest-ERROR-ID.properties similarity index 100% rename from sia2/src/intTest/resources/disable-SyncTest-ERROR-ID.properties rename to dap/src/intTest/resources/disable-SyncTest-ERROR-ID.properties diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-INSTRUMENT.properties b/dap/src/intTest/resources/disable-SyncTest-ERROR-INSTRUMENT.properties similarity index 100% rename from sia2/src/intTest/resources/disable-SyncTest-ERROR-INSTRUMENT.properties rename to dap/src/intTest/resources/disable-SyncTest-ERROR-INSTRUMENT.properties diff --git a/sia2/src/intTest/resources/disable-SyncTest-ERROR-TARGET.properties b/dap/src/intTest/resources/disable-SyncTest-ERROR-TARGET.properties similarity index 100% rename from sia2/src/intTest/resources/disable-SyncTest-ERROR-TARGET.properties rename to dap/src/intTest/resources/disable-SyncTest-ERROR-TARGET.properties diff --git a/sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java b/dap/src/main/java/org/opencadc/dap/AdqlQueryGenerator.java similarity index 97% rename from sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java rename to dap/src/main/java/org/opencadc/dap/AdqlQueryGenerator.java index 1e0a8664..7340dac7 100644 --- a/sia2/src/main/java/org/opencadc/sia2/AdqlQueryGenerator.java +++ b/dap/src/main/java/org/opencadc/dap/AdqlQueryGenerator.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.dali.Circle; import ca.nrc.cadc.dali.Interval; @@ -90,19 +90,21 @@ public class AdqlQueryGenerator { private static Logger log = Logger.getLogger(AdqlQueryGenerator.class); - private String tableName; - private Map> queryParams; + private final boolean sia2mode; + private final String tableName; + private final Map> queryParams; /** - * The input SIA query parameters as structured by the ParamExtractor in cadcDALI. + * The input query parameters as structured by the ParamExtractor in cadc-dali. * * @param query query input parameters * @param tableName ivoa.ObsCore table name * @see ca.nrc.cadc.dali.ParamExtractor */ - public AdqlQueryGenerator(Map> query, String tableName) { + public AdqlQueryGenerator(Map> query, String tableName, boolean sia2mode) { this.tableName = tableName; this.queryParams = query; + this.sia2mode = sia2mode; } /** @@ -125,7 +127,7 @@ protected String getQuery() { query.append(tableName); query.append(" WHERE dataproduct_type IN ( 'image', 'cube' )"); - SiaParamValidator sia = new SiaParamValidator(); + DapParamValidator sia = new DapParamValidator(sia2mode); List pos = sia.validatePOS(queryParams); if (!pos.isEmpty()) { boolean needOr = false; @@ -257,9 +259,6 @@ protected String getQuery() { List instruments = sia.validateINSTRUMENT(queryParams); addStringListConstraint(query, "instrument_name", instruments); - List dptypes = sia.validateDPTYPE(queryParams); - addStringListConstraint(query, "dataproduct_type", dptypes); - List calibs = sia.validateCALIB(queryParams); addIntegerListConstraint(query, "calib_level", calibs); @@ -275,6 +274,9 @@ protected String getQuery() { List formats = sia.validateFORMAT(queryParams); addStringListConstraint(query, "access_format", formats); + List dptypes = sia.validateDPTYPE(queryParams); + addStringListConstraint(query, "dataproduct_type", dptypes); + return query.toString(); } diff --git a/sia2/src/main/java/org/opencadc/sia2/SiaConfig.java b/dap/src/main/java/org/opencadc/dap/DapConfig.java similarity index 91% rename from sia2/src/main/java/org/opencadc/sia2/SiaConfig.java rename to dap/src/main/java/org/opencadc/dap/DapConfig.java index 1837104f..c745c158 100644 --- a/sia2/src/main/java/org/opencadc/sia2/SiaConfig.java +++ b/dap/src/main/java/org/opencadc/dap/DapConfig.java @@ -3,7 +3,7 @@ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * -* (c) 2023. (c) 2023. +* (c) 2024. (c) 2024. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 @@ -63,9 +63,9 @@ * . * ************************************************************************ -*/ + */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.auth.AuthMethod; import ca.nrc.cadc.net.ResourceNotFoundException; @@ -79,30 +79,31 @@ import java.net.URISyntaxException; import java.net.URL; import org.apache.log4j.Logger; -import org.opencadc.tap.TapClient; /** * * @author pdowler */ -public class SiaConfig { - private static final Logger log = Logger.getLogger(SiaConfig.class); +public class DapConfig { + + private static final Logger log = Logger.getLogger(DapConfig.class); + + private static final String CONFIG = "dap.properties"; + private static final String STD_OBSCORE_TABLE = "ivoa.ObsCore"; - private static final String CONFIG = "sia2.properties"; - - private static final String BASE_KEY = "org.opencadc.sia2"; + private static final String BASE_KEY = "org.opencadc.dap"; private static final String QUERY_KEY = BASE_KEY + ".queryService"; private static final String TABLE_KEY = BASE_KEY + ".table"; - + private final URI queryService; private final String tableName; - - public SiaConfig() { + + public DapConfig() { StringBuilder sb = new StringBuilder(); try { PropertiesReader r = new PropertiesReader(CONFIG); MultiValuedProperties props = r.getAllProperties(); - + String qs = props.getFirstPropertyValue(QUERY_KEY); URI qsURI = null; sb.append("\n\t").append(QUERY_KEY).append(" - "); @@ -116,31 +117,31 @@ public SiaConfig() { sb.append("ERROR invalid URI: " + qs); } } - + if (qsURI == null) { throw new InvalidConfigException("invalid config: " + sb.toString()); } this.queryService = qsURI; - String tn = props.getFirstPropertyValue(TABLE_KEY); - if (tn == null) { - this.tableName = "ivoa.ObsCore"; - } else { + String tn = props.getFirstPropertyValue(TABLE_KEY); + if (tn == null) { + this.tableName = STD_OBSCORE_TABLE; + } else { this.tableName = tn; - } + } } catch (InvalidConfigException ex) { throw ex; } } - + public String getTableName() { - return tableName; + return tableName; } public URI getQueryService() { return queryService; } - + public URL getTapSyncURL() throws MalformedURLException, ResourceNotFoundException { if (queryService.getScheme().equals("ivo")) { // registry lookup diff --git a/sia2/src/main/java/org/opencadc/sia2/SiaParamValidator.java b/dap/src/main/java/org/opencadc/dap/DapParamValidator.java similarity index 93% rename from sia2/src/main/java/org/opencadc/sia2/SiaParamValidator.java rename to dap/src/main/java/org/opencadc/dap/DapParamValidator.java index f41ecf72..b3515e12 100644 --- a/sia2/src/main/java/org/opencadc/sia2/SiaParamValidator.java +++ b/dap/src/main/java/org/opencadc/dap/DapParamValidator.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.dali.CommonParamValidator; import ca.nrc.cadc.dali.Interval; @@ -80,9 +80,9 @@ * * @author pdowler */ -public class SiaParamValidator extends CommonParamValidator { +public class DapParamValidator extends CommonParamValidator { - private static final Logger log = Logger.getLogger(SiaParamValidator.class); + private static final Logger log = Logger.getLogger(DapParamValidator.class); // POS, BAND, TIME, POL, ID params inherited from dali.common.ParamValiator @@ -107,9 +107,12 @@ public class SiaParamValidator extends CommonParamValidator { CALIB, TARGET, TIMERES, SPECRP, FORMAT); // allowed data product types are image and cube - static final List ALLOWED_DPTYPES = Arrays.asList("cube", "image"); + static final List SIA2_DPTYPES = Arrays.asList("cube", "image"); - public SiaParamValidator() { + private final boolean sia2mode; + + public DapParamValidator(boolean sia2mode) { + this.sia2mode = sia2mode; } private String scalar2interval(String s) { @@ -145,7 +148,10 @@ public List validateINSTRUMENT(Map> params) { } public List validateDPTYPE(Map> params) { - return validateString(DPTYPE, params, ALLOWED_DPTYPES); + if (sia2mode) { + return validateString(DPTYPE, params, SIA2_DPTYPES); + } + return validateString(DPTYPE, params, null); // allow all values } public List validateCALIB(Map> params) { diff --git a/sia2/src/main/java/org/opencadc/sia2/SiaRunner.java b/dap/src/main/java/org/opencadc/dap/DapQueryRunner.java similarity index 93% rename from sia2/src/main/java/org/opencadc/sia2/SiaRunner.java rename to dap/src/main/java/org/opencadc/dap/DapQueryRunner.java index d5625d8a..62f79f24 100644 --- a/sia2/src/main/java/org/opencadc/sia2/SiaRunner.java +++ b/dap/src/main/java/org/opencadc/dap/DapQueryRunner.java @@ -3,7 +3,7 @@ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * -* (c) 2014. (c) 2014. +* (c) 2024. (c) 2024. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.dali.MaxRecValidator; import ca.nrc.cadc.dali.ParamExtractor; @@ -92,7 +92,7 @@ import org.apache.log4j.Logger; /** - * Standard JobRunner implementation for SIA-2.0 services. This implementation + * Standard JobRunner implementation for DAP-2.x services. This implementation * makes the following assumptions: * *
    @@ -102,13 +102,15 @@ * * @author pdowler */ -public class SiaRunner implements JobRunner { +public class DapQueryRunner implements JobRunner { - private static Logger log = Logger.getLogger(SiaRunner.class); + private static Logger log = Logger.getLogger(DapQueryRunner.class); private static final Integer DEF_MAXREC = 1000; private static final Integer MAX_MAXREC = null; + private final boolean sia2mode = true; + private Job job; private JobUpdater jobUpdater; private SyncOutput syncOutput; @@ -127,9 +129,7 @@ public void setSyncOutput(SyncOutput so) { } public void run() { - log.debug("RUN SiaRunner: " + job.ownerSubject); - - logInfo = new JobLogInfo(job); + this.logInfo = new JobLogInfo(job); String startMessage = logInfo.start(); log.info(startMessage); @@ -162,20 +162,20 @@ private void doit() { mv.setMaxValue(MAX_MAXREC); Integer maxrec = mv.validate(); - SiaConfig conf = new SiaConfig(); - ParamExtractor pe = new ParamExtractor(SiaParamValidator.QUERY_PARAMS); + DapConfig conf = new DapConfig(); + ParamExtractor pe = new ParamExtractor(DapParamValidator.QUERY_PARAMS); Map> queryParams = pe.getParameters(job.getParameterList()); // Get the ADQL request parameters. - AdqlQueryGenerator queryGenerator = new AdqlQueryGenerator(queryParams, conf.getTableName()); + AdqlQueryGenerator queryGenerator = new AdqlQueryGenerator(queryParams, conf.getTableName(), sia2mode); Map parameters = queryGenerator.getParameterMap(); - parameters.put("FORMAT", VOTableWriter.CONTENT_TYPE); + parameters.put("RESPONSEFORMAT", VOTableWriter.CONTENT_TYPE); if (maxrec != null) { parameters.put("MAXREC", maxrec); } // the implementation assumes that the /tap/sync service follows the - // POST-redirect-GET (PrG) pattern; cadcUWS SyncServlet does + // POST-redirect-GET (PrG) pattern; cadc-uws-server does URL tapSyncURL = conf.getTapSyncURL(); // POST ADQL query to TAP but do not follow redirect to execute it. diff --git a/sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java b/dap/src/main/java/org/opencadc/dap/QueryJobManager.java similarity index 97% rename from sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java rename to dap/src/main/java/org/opencadc/dap/QueryJobManager.java index 3b5225ef..a3fec9a1 100644 --- a/sia2/src/main/java/org/opencadc/sia2/QueryJobManager.java +++ b/dap/src/main/java/org/opencadc/dap/QueryJobManager.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.auth.AuthenticationUtil; import ca.nrc.cadc.auth.IdentityManager; @@ -96,7 +96,7 @@ public QueryJobManager() { JobPersistence jobPersist = new PostgresJobPersistence(im); // exec jobs in request thread using custom SiaRunner - JobExecutor jobExec = new SyncJobExecutor(jobPersist, SiaRunner.class); + JobExecutor jobExec = new SyncJobExecutor(jobPersist, DapQueryRunner.class); super.setJobPersistence(jobPersist); super.setJobExecutor(jobExec); diff --git a/sia2/src/main/java/org/opencadc/sia2/ServiceAvailability.java b/dap/src/main/java/org/opencadc/dap/ServiceAvailability.java similarity index 99% rename from sia2/src/main/java/org/opencadc/sia2/ServiceAvailability.java rename to dap/src/main/java/org/opencadc/dap/ServiceAvailability.java index 78d7b3ce..71652394 100644 --- a/sia2/src/main/java/org/opencadc/sia2/ServiceAvailability.java +++ b/dap/src/main/java/org/opencadc/dap/ServiceAvailability.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.auth.AuthMethod; import ca.nrc.cadc.net.ResourceNotFoundException; @@ -192,7 +192,7 @@ public Availability getStatus() { log.debug("not found: " + Standards.UMS_USERS_01.toASCIIString()); } - SiaConfig conf = new SiaConfig(); + DapConfig conf = new DapConfig(); url = conf.getAvailailityURL(); if (url != null) { checkResource = new CheckWebService(url); diff --git a/sia2/src/main/java/org/opencadc/sia2/UWSInitAction.java b/dap/src/main/java/org/opencadc/dap/UWSInitAction.java similarity index 99% rename from sia2/src/main/java/org/opencadc/sia2/UWSInitAction.java rename to dap/src/main/java/org/opencadc/dap/UWSInitAction.java index c34d2c20..989af60c 100644 --- a/sia2/src/main/java/org/opencadc/sia2/UWSInitAction.java +++ b/dap/src/main/java/org/opencadc/dap/UWSInitAction.java @@ -65,7 +65,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.db.DBUtil; import ca.nrc.cadc.rest.InitAction; diff --git a/sia2/src/main/java/org/opencadc/sia2/UsageError.java b/dap/src/main/java/org/opencadc/dap/UsageError.java similarity index 99% rename from sia2/src/main/java/org/opencadc/sia2/UsageError.java rename to dap/src/main/java/org/opencadc/dap/UsageError.java index 998ca3c9..d3dcd586 100644 --- a/sia2/src/main/java/org/opencadc/sia2/UsageError.java +++ b/dap/src/main/java/org/opencadc/dap/UsageError.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; /** * diff --git a/sia2/src/main/webapp/META-INF/context.xml b/dap/src/main/webapp/META-INF/context.xml similarity index 100% rename from sia2/src/main/webapp/META-INF/context.xml rename to dap/src/main/webapp/META-INF/context.xml diff --git a/sia2/src/main/webapp/WEB-INF/web.xml b/dap/src/main/webapp/WEB-INF/web.xml similarity index 94% rename from sia2/src/main/webapp/WEB-INF/web.xml rename to dap/src/main/webapp/WEB-INF/web.xml index 3b819492..9ff1ed5b 100644 --- a/sia2/src/main/webapp/WEB-INF/web.xml +++ b/dap/src/main/webapp/WEB-INF/web.xml @@ -23,7 +23,8 @@ logLevelPackages - org.opencadc.sia2 + org.opencadc.dap + ca.nrc.cadc.dali ca.nrc.cadc.uws ca.nrc.cadc.rest ca.nrc.cadc.vosi @@ -56,7 +57,7 @@ ca.nrc.cadc.uws.server.JobManager - org.opencadc.sia2.QueryJobManager + org.opencadc.dap.QueryJobManager @@ -68,7 +69,7 @@ ca.nrc.cadc.vosi.AvailabilityPlugin - org.opencadc.sia2.ServiceAvailability + org.opencadc.dap.ServiceAvailability diff --git a/sia2/src/main/webapp/capabilities.xml b/dap/src/main/webapp/capabilities.xml similarity index 81% rename from sia2/src/main/webapp/capabilities.xml rename to dap/src/main/webapp/capabilities.xml index b4a5c455..7d56953c 100644 --- a/sia2/src/main/webapp/capabilities.xml +++ b/dap/src/main/webapp/capabilities.xml @@ -6,19 +6,19 @@ - https://replace.me.com/sia2/capabilities + https://replace.me.com/dap/capabilities - https://replace.me.com/sia2/availability + https://replace.me.com/dap/availability - https://replace.me.com/sia2/logControl + https://replace.me.com/dap/logControl @@ -28,7 +28,7 @@ - https://replace.me.com/sia2/query + https://replace.me.com/dap/query diff --git a/sia2/src/main/webapp/index.html b/dap/src/main/webapp/index.html similarity index 100% rename from sia2/src/main/webapp/index.html rename to dap/src/main/webapp/index.html diff --git a/sia2/src/main/webapp/service.json b/dap/src/main/webapp/service.json similarity index 100% rename from sia2/src/main/webapp/service.json rename to dap/src/main/webapp/service.json diff --git a/sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java b/dap/src/test/java/org/opencadc/dap/AdqlQueryGeneratorTest.java similarity index 97% rename from sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java rename to dap/src/test/java/org/opencadc/dap/AdqlQueryGeneratorTest.java index 5c3ac582..ef087254 100644 --- a/sia2/src/test/java/org/opencadc/sia2/AdqlQueryGeneratorTest.java +++ b/dap/src/test/java/org/opencadc/dap/AdqlQueryGeneratorTest.java @@ -67,7 +67,7 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; import ca.nrc.cadc.util.CaseInsensitiveStringComparator; import ca.nrc.cadc.util.Log4jInit; @@ -89,9 +89,11 @@ public class AdqlQueryGeneratorTest { private static final Logger log = Logger.getLogger(AdqlQueryGeneratorTest.class); static { - Log4jInit.setLevel("ca.nrc.cadc.sia2", Level.INFO); + Log4jInit.setLevel("org.opencadc.dap", Level.INFO); } + boolean sia2mode = true; + //@Test public void testTemplate() { @@ -108,7 +110,7 @@ public void testNoParams() { try { Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore", sia2mode); Map tapParams = gen.getParameterMap(); String lang = (String) tapParams.get("LANG"); @@ -132,7 +134,7 @@ public void testScalarInterval() { params.put("BAND", Arrays.asList("550e-9")); params.put("TIME", Arrays.asList("54321.0")); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore", sia2mode); Map tapParams = gen.getParameterMap(); String lang = (String) tapParams.get("LANG"); @@ -176,7 +178,7 @@ public void testSingleParams() { params.put("SPECRP", Arrays.asList("-inf 500")); params.put("FORMAT", Arrays.asList("application/fits")); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore", sia2mode); String adql = gen.getQuery(); log.info("testSingleParams ADQL:\n" + adql); @@ -233,7 +235,7 @@ public void testMultipleParams() { params.put("SPECRP", Arrays.asList("-Inf 500", "200 300")); params.put("FORMAT", Arrays.asList("application/fits", "text/xml")); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore", sia2mode); String adql = gen.getQuery(); log.info("testMultipleParams ADQL:\n" + adql); @@ -273,7 +275,7 @@ public void testCoordRanges() { Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); params.put("POS", Arrays.asList("RANGE 0 360 -2 2", "RANGE 10 20 -90 90", "RANGE 1 2 3 4")); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore"); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "ivoa.ObsCore", sia2mode); String adql = gen.getQuery(); log.info("testCoordRanges ADQL:\n" + adql); @@ -292,7 +294,7 @@ public void testAltTableNames() { Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); params.put("POS", Arrays.asList("RANGE 0 360 -2 2", "RANGE 10 20 -90 90", "RANGE 1 2 3 4")); - AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "schema.TableName"); + AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "schema.TableName", sia2mode); String adql = gen.getQuery(); log.info("testCoordRanges ADQL:\n" + adql); diff --git a/sia2/src/test/java/org/opencadc/sia2/SiaParamValidatorTest.java b/dap/src/test/java/org/opencadc/dap/DapParamValidatorTest.java similarity index 80% rename from sia2/src/test/java/org/opencadc/sia2/SiaParamValidatorTest.java rename to dap/src/test/java/org/opencadc/dap/DapParamValidatorTest.java index b269f217..c9525933 100644 --- a/sia2/src/test/java/org/opencadc/sia2/SiaParamValidatorTest.java +++ b/dap/src/test/java/org/opencadc/dap/DapParamValidatorTest.java @@ -67,8 +67,9 @@ ************************************************************************ */ -package org.opencadc.sia2; +package org.opencadc.dap; +import org.opencadc.dap.DapParamValidator; import ca.nrc.cadc.dali.DoubleInterval; import ca.nrc.cadc.util.CaseInsensitiveStringComparator; import ca.nrc.cadc.util.Log4jInit; @@ -86,15 +87,15 @@ * * @author pdowler */ -public class SiaParamValidatorTest { +public class DapParamValidatorTest { - private static final Logger log = Logger.getLogger(SiaParamValidatorTest.class); + private static final Logger log = Logger.getLogger(DapParamValidatorTest.class); static { Log4jInit.setLevel("ca.nrc.cadc.sia2", Level.INFO); } - SiaParamValidator sia = new SiaParamValidator(); + DapParamValidator dapParamValidator = new DapParamValidator(false); // default: DAP mode @Test public void testValidateFOV() { @@ -102,12 +103,12 @@ public void testValidateFOV() { String[] testParams = new String[]{"FOV", "fov", "FoV"}; try { - List empty = sia.validateFOV(null); // compile and null arg check + List empty = dapParamValidator.validateFOV(null); // compile and null arg check Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method m = SiaParamValidator.class.getMethod("validateFOV", Map.class); - doValidateNumeric(m, "FOV", testParams); + Method m = DapParamValidator.class.getMethod("validateFOV", Map.class); + doValidateNumeric(dapParamValidator, m, "FOV", testParams); // invalid: code is more or less tested already in testValidateBand } catch (Exception unexpected) { @@ -122,12 +123,12 @@ public void testValidateSPATRES() { String[] testParams = new String[]{"SPATRES", "spatres", "SpAtReS"}; try { - List empty = sia.validateSPATRES(null); // compile and null arg check + List empty = dapParamValidator.validateSPATRES(null); // compile and null arg check Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method m = SiaParamValidator.class.getMethod("validateSPATRES", Map.class); - doValidateNumeric(m, "SPATRES", testParams); + Method m = DapParamValidator.class.getMethod("validateSPATRES", Map.class); + doValidateNumeric(dapParamValidator, m, "SPATRES", testParams); // invalid: code is more or less tested already in testValidateBand } catch (Exception unexpected) { @@ -142,12 +143,12 @@ public void testValidateEXPTIME() { String[] testParams = new String[]{"EXPTIME", "exptime", "ExPtImE"}; try { - List empty = sia.validateEXPTIME(null); // compile and null arg check + List empty = dapParamValidator.validateEXPTIME(null); // compile and null arg check Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method m = SiaParamValidator.class.getMethod("validateEXPTIME", Map.class); - doValidateNumeric(m, "EXPTIME", testParams); + Method m = DapParamValidator.class.getMethod("validateEXPTIME", Map.class); + doValidateNumeric(dapParamValidator, m, "EXPTIME", testParams); // invalid: code is more or less tested already in testValidateBand } catch (Exception unexpected) { @@ -162,12 +163,12 @@ public void testValidateCOLLECTION() { String[] testParams = new String[]{"COLLECTION", "collection", "CoLlEcTiOn"}; // null arg check - List empty = sia.validateCOLLECTION(null); + List empty = dapParamValidator.validateCOLLECTION(null); Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method method = SiaParamValidator.class.getMethod("validateCOLLECTION", Map.class); - doValidateString(method, testParams, null); + Method m = DapParamValidator.class.getMethod("validateCOLLECTION", Map.class); + doValidateString(dapParamValidator, m, testParams, null); } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); @@ -180,12 +181,12 @@ public void testValidateFACILITY() { String[] testParams = new String[]{"FACILITY", "facility", "FaCiLiTy"}; // null arg check - List empty = sia.validateFACILITY(null); + List empty = dapParamValidator.validateFACILITY(null); Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method method = SiaParamValidator.class.getMethod("validateFACILITY", Map.class); - doValidateString(method, testParams, null); + Method m = DapParamValidator.class.getMethod("validateFACILITY", Map.class); + doValidateString(dapParamValidator, m, testParams, null); } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); @@ -198,12 +199,12 @@ public void testValidateINSTRUMENT() { String[] testParams = new String[]{"INSTRUMENT", "instrument", "InStRuMeNt"}; // null arg check - List empty = sia.validateINSTRUMENT(null); + List empty = dapParamValidator.validateINSTRUMENT(null); Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method method = SiaParamValidator.class.getMethod("validateINSTRUMENT", Map.class); - doValidateString(method, testParams, null); + Method m = DapParamValidator.class.getMethod("validateINSTRUMENT", Map.class); + doValidateString(dapParamValidator, m, testParams, null); } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); @@ -212,19 +213,21 @@ public void testValidateINSTRUMENT() { @Test public void testValidateDPTYPE() { + // test invalid value: SIAv2 mode + DapParamValidator sia = new DapParamValidator(true); + try { String[] testParams = new String[]{"DPTYPE", "dptype", "DpTyPe"}; String[] testValues = new String[]{"cube", "image"}; // null arg check - List empty = sia.validateDPTYPE(null); + List empty = dapParamValidator.validateDPTYPE(null); Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method method = SiaParamValidator.class.getMethod("validateDPTYPE", Map.class); - doValidateString(method, testParams, testValues); - - // test invalid value + Method m = DapParamValidator.class.getMethod("validateDPTYPE", Map.class); + doValidateString(sia, m, testParams, testValues); + Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); List vals = new ArrayList(); vals.add("FOO"); @@ -236,6 +239,11 @@ public void testValidateDPTYPE() { } catch (IllegalArgumentException expected) { log.debug("caught expected: " + expected); } + + // DAP validator: no invalid values + testValues = new String[] {"cube", "image", "spectrum", "timeseries", "something-else" }; + doValidateString(dapParamValidator, m, testParams, testValues); + } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); @@ -247,12 +255,12 @@ public void testValidateCALIB() { try { String[] testParams = new String[]{"CALIB", "calib", "CaLiB"}; - List empty = sia.validateCALIB(null); // compile and null arg check + List empty = dapParamValidator.validateCALIB(null); // compile and null arg check Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method m = SiaParamValidator.class.getMethod("validateCALIB", Map.class); - doValidateInteger(m, "CALIB", testParams); + Method m = DapParamValidator.class.getMethod("validateCALIB", Map.class); + doValidateInteger(dapParamValidator, m, "CALIB", testParams); } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); @@ -265,12 +273,12 @@ public void testValidateTARGET() { String[] testParams = new String[]{"TARGET", "target", "TaRgEt"}; // null arg check - List empty = sia.validateTARGET(null); + List empty = dapParamValidator.validateTARGET(null); Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method method = SiaParamValidator.class.getMethod("validateTARGET", Map.class); - doValidateString(method, testParams, null); + Method m = DapParamValidator.class.getMethod("validateTARGET", Map.class); + doValidateString(dapParamValidator, m, testParams, null); } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); @@ -282,12 +290,12 @@ public void testValidateTIMERES() { try { String[] testParams = new String[]{"TIMERES", "timeres", "TiMeReS"}; - List empty = sia.validateTIMERES(null); // compile and null arg check + List empty = dapParamValidator.validateTIMERES(null); // compile and null arg check Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method m = SiaParamValidator.class.getMethod("validateTIMERES", Map.class); - doValidateNumeric(m, "TIMERES", testParams); + Method m = DapParamValidator.class.getMethod("validateTIMERES", Map.class); + doValidateNumeric(dapParamValidator, m, "TIMERES", testParams); } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); @@ -299,12 +307,12 @@ public void testValidateSPECRP() { try { String[] testParams = new String[]{"SPECRP", "specrp", "SpEcRp"}; - List empty = sia.validateSPECRP(null); // compile and null arg check + List empty = dapParamValidator.validateSPECRP(null); // compile and null arg check Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method m = SiaParamValidator.class.getMethod("validateSPECRP", Map.class); - doValidateNumeric(m, "SPECRP", testParams); + Method m = DapParamValidator.class.getMethod("validateSPECRP", Map.class); + doValidateNumeric(dapParamValidator, m, "SPECRP", testParams); } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); @@ -317,12 +325,12 @@ public void testValidateFORMAT() { String[] testParams = new String[]{"FORMAT", "format", "FoRmAt"}; // null arg check - List empty = sia.validateFORMAT(null); + List empty = dapParamValidator.validateFORMAT(null); Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); - Method method = SiaParamValidator.class.getMethod("validateFORMAT", Map.class); - doValidateString(method, testParams, null); + Method m = DapParamValidator.class.getMethod("validateFORMAT", Map.class); + doValidateString(dapParamValidator, m, testParams, null); } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); @@ -332,7 +340,7 @@ public void testValidateFORMAT() { @Test public void testValidateCustomParam() { try { - List empty = sia.validateString("FOO", null, null); + List empty = dapParamValidator.validateString("FOO", null, null); Assert.assertNotNull(empty); Assert.assertTrue(empty.isEmpty()); @@ -340,7 +348,7 @@ public void testValidateCustomParam() { List vals = new ArrayList(); vals.add("abc"); params.put("FOO", vals); - List strs = sia.validateString("FOO", params, null); + List strs = dapParamValidator.validateString("FOO", params, null); Assert.assertNotNull(strs); Assert.assertEquals(1, strs.size()); String s = strs.get(0); @@ -351,7 +359,7 @@ public void testValidateCustomParam() { } } - public void doValidateString(Method testMethod, String[] testParams, String[] testValues) + public void doValidateString(DapParamValidator instance, Method testMethod, String[] testParams, String[] testValues) throws Exception { if (testValues == null) { testValues = new String[]{ @@ -372,14 +380,14 @@ public void doValidateString(Method testMethod, String[] testParams, String[] te vals.add(testValues[j]); } params.put(tp, vals); - List pols = (List) testMethod.invoke(sia, params); + List pols = (List) testMethod.invoke(instance, params); Assert.assertNotNull(pols); Assert.assertEquals(len[i], pols.size()); } } } - public void doValidateInteger(Method m, String paramName, String[] testParams) { + public void doValidateInteger(DapParamValidator instance, Method m, String paramName, String[] testParams) { String[] testValues = new String[]{ "0", "1", @@ -395,7 +403,7 @@ public void doValidateInteger(Method m, String paramName, String[] testParams) { expected.add(tv); params.put(tp, expected); - List actual = (List) m.invoke(sia, params); + List actual = (List) m.invoke(instance, params); Assert.assertNotNull(actual); Assert.assertEquals(1, actual.size()); @@ -411,7 +419,7 @@ public void doValidateInteger(Method m, String paramName, String[] testParams) { expected.add(e); } params.put(paramName, expected); - List actual = (List) m.invoke(sia, params); + List actual = (List) m.invoke(instance, params); Assert.assertNotNull(actual); Assert.assertEquals(expected.size(), actual.size()); for (String e : expected) { @@ -424,7 +432,7 @@ public void doValidateInteger(Method m, String paramName, String[] testParams) { } } - public void doValidateNumeric(Method m, String paramName, String[] testParams) { + public void doValidateNumeric(DapParamValidator instance, Method m, String paramName, String[] testParams) { String LB = "12.3 +Inf"; String UB = "-Inf 34.5"; String OPEN = "-Inf +Inf"; @@ -447,7 +455,7 @@ public void doValidateNumeric(Method m, String paramName, String[] testParams) { vals.add(tv); params.put(tp, vals); - List ranges = (List) m.invoke(sia, params); + List ranges = (List) m.invoke(instance, params); Assert.assertNotNull(ranges); Assert.assertEquals(1, ranges.size()); @@ -479,7 +487,7 @@ public void doValidateNumeric(Method m, String paramName, String[] testParams) { vals.add(testValues[i]); } params.put(paramName, vals); - List ranges = (List) m.invoke(sia, params); + List ranges = (List) m.invoke(instance, params); Assert.assertNotNull(ranges); Assert.assertEquals(3, ranges.size()); for (DoubleInterval r : ranges) { diff --git a/sia2/Dockerfile b/sia2/Dockerfile deleted file mode 100644 index 8e8876a1..00000000 --- a/sia2/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM images.opencadc.org/library/cadc-tomcat:1 - -COPY build/libs/sia2.war /usr/share/tomcat/webapps From 473e52a0ac671db3fb6220d8876c396e1680cea1 Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Mon, 7 Oct 2024 14:24:34 -0700 Subject: [PATCH 08/11] complete DAP prototype with SIAv2 mode --- dap/build.gradle | 2 +- .../java/org/opencadc/dap/Constants.java} | 30 ++--- .../org/opencadc/dap/DapQueryErrorTest.java | 127 ++++++++++++++++++ .../org/opencadc/dap/DapQueryGetTest.java | 125 +++++++++++++++++ .../org/opencadc/dap/SiaQuery2ErrorTest.java | 4 +- .../org/opencadc/dap/SiaQuery2GetTest.java | 4 +- .../opencadc/dap/VosiAvailabilityTest.java | 9 +- .../opencadc/dap/VosiCapabilitiesTest.java | 9 +- ... disable-SyncTest-ERROR-DPTYPE.properties} | 0 .../org/opencadc/dap/AdqlQueryGenerator.java | 60 +++++---- .../main/java/org/opencadc/dap/DapConfig.java | 9 ++ .../java/org/opencadc/dap/DapInitAction.java | 114 ++++++++++++++++ .../java/org/opencadc/dap/DapQueryRunner.java | 4 +- dap/src/main/webapp/META-INF/context.xml | 6 +- dap/src/main/webapp/WEB-INF/web.xml | 33 ++--- dap/src/main/webapp/capabilities.xml | 3 +- .../opencadc/dap/AdqlQueryGeneratorTest.java | 3 - 17 files changed, 447 insertions(+), 95 deletions(-) rename dap/src/{main/java/org/opencadc/dap/UWSInitAction.java => intTest/java/org/opencadc/dap/Constants.java} (84%) create mode 100644 dap/src/intTest/java/org/opencadc/dap/DapQueryErrorTest.java create mode 100644 dap/src/intTest/java/org/opencadc/dap/DapQueryGetTest.java rename dap/src/intTest/resources/{SyncTest-ERROR-DPTYPE.properties => disable-SyncTest-ERROR-DPTYPE.properties} (100%) create mode 100644 dap/src/main/java/org/opencadc/dap/DapInitAction.java diff --git a/dap/build.gradle b/dap/build.gradle index c92917f9..e1f0b31c 100644 --- a/dap/build.gradle +++ b/dap/build.gradle @@ -34,7 +34,7 @@ dependencies { compile 'org.opencadc:cadc-tap:[1.0,2.0)' compile 'org.opencadc:cadc-vosi:[1.4.3,2.0)' - runtime 'org.opencadc:cadc-registry:[1.4.6,)' + runtime 'org.opencadc:cadc-registry:[1.7.7,)' runtime 'org.opencadc:cadc-log:[1.0,)' runtime 'org.opencadc:cadc-gms:[1.0.7,2.0)' runtime 'org.opencadc:cadc-access-control-identity:[1.1.0,)' diff --git a/dap/src/main/java/org/opencadc/dap/UWSInitAction.java b/dap/src/intTest/java/org/opencadc/dap/Constants.java similarity index 84% rename from dap/src/main/java/org/opencadc/dap/UWSInitAction.java rename to dap/src/intTest/java/org/opencadc/dap/Constants.java index 989af60c..71a6ab13 100644 --- a/dap/src/main/java/org/opencadc/dap/UWSInitAction.java +++ b/dap/src/intTest/java/org/opencadc/dap/Constants.java @@ -3,7 +3,7 @@ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * -* (c) 2022. (c) 2022. +* (c) 2024. (c) 2024. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 @@ -67,33 +67,19 @@ package org.opencadc.dap; -import ca.nrc.cadc.db.DBUtil; -import ca.nrc.cadc.rest.InitAction; -import ca.nrc.cadc.uws.server.impl.InitDatabaseUWS; -import javax.sql.DataSource; +import java.net.URI; import org.apache.log4j.Logger; /** * * @author pdowler */ -public class UWSInitAction extends InitAction { - private static final Logger log = Logger.getLogger(UWSInitAction.class); +public class Constants { + private static final Logger log = Logger.getLogger(Constants.class); - public UWSInitAction() { - } - - @Override - public void doInit() { - try { - DataSource uws = DBUtil.findJNDIDataSource("jdbc/uws"); - InitDatabaseUWS uwsi = new InitDatabaseUWS(uws, null, "uws"); - uwsi.doInit(); - log.info("init uws: OK"); - } catch (Exception ex) { - throw new RuntimeException("INIT FAIL", ex); - } - } - + public static URI DAP_RESOURCE_ID = URI.create("ivo://opencadc.org/dap"); + public static URI SIA_RESOURCE_ID = URI.create("ivo://opencadc.org/sia"); + private Constants() { + } } diff --git a/dap/src/intTest/java/org/opencadc/dap/DapQueryErrorTest.java b/dap/src/intTest/java/org/opencadc/dap/DapQueryErrorTest.java new file mode 100644 index 00000000..093a964b --- /dev/null +++ b/dap/src/intTest/java/org/opencadc/dap/DapQueryErrorTest.java @@ -0,0 +1,127 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.dap; + +import ca.nrc.cadc.conformance.uws2.JobResultWrapper; +import ca.nrc.cadc.conformance.uws2.SyncUWSTest; +import ca.nrc.cadc.dali.tables.votable.VOTableDocument; +import ca.nrc.cadc.reg.Standards; +import ca.nrc.cadc.util.FileUtil; +import ca.nrc.cadc.util.Log4jInit; +import java.io.File; +import java.net.URI; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.Assert; + +/** + * + * @author pdowler + */ +public class DapQueryErrorTest extends SyncUWSTest { + + private static final Logger log = Logger.getLogger(DapQueryErrorTest.class); + + static { + Log4jInit.setLevel("org.opencadc.dap", Level.INFO); + Log4jInit.setLevel("ca.nrc.cadc.conformance.uws2", Level.INFO); + } + + public DapQueryErrorTest() { + super(Constants.DAP_RESOURCE_ID, Standards.DAP_QUERY_21); + + File testFile = FileUtil.getFileFromResource("SyncTest-ERROR-BAND.properties", DapQueryErrorTest.class); + if (testFile.exists()) { + File testDir = testFile.getParentFile(); + super.setPropertiesDir(testDir, "SyncTest-ERROR"); + } + } + + @Override + protected void validateResponse(JobResultWrapper result) { + Assert.assertEquals(400, result.responseCode); + Assert.assertEquals("application/x-votable+xml", result.contentType); + + try { + Assert.assertNotNull(result.syncOutput); + //VOTableDocument vot = VOTableHandler.getVOTable(result.syncOutput); + // because cadc-util HttpTransfer reads the error body and stores it in the Exception + VOTableDocument vot = VOTableHandler.getVOTable(result.throwable); + log.info(result.name + ": found valid VOTable"); + + String queryStatus = VOTableHandler.getQueryStatus(vot); + Assert.assertNotNull("QUERY_STATUS", queryStatus); + Assert.assertEquals("ERROR", queryStatus); + } catch (Exception ex) { + log.error("unexpected exception", ex); + Assert.fail("unexpected exception: " + ex); + } + } +} diff --git a/dap/src/intTest/java/org/opencadc/dap/DapQueryGetTest.java b/dap/src/intTest/java/org/opencadc/dap/DapQueryGetTest.java new file mode 100644 index 00000000..6e533a17 --- /dev/null +++ b/dap/src/intTest/java/org/opencadc/dap/DapQueryGetTest.java @@ -0,0 +1,125 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2023. (c) 2023. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +* $Revision: 5 $ +* +************************************************************************ + */ + +package org.opencadc.dap; + +import ca.nrc.cadc.conformance.uws2.JobResultWrapper; +import ca.nrc.cadc.conformance.uws2.SyncUWSTest; +import ca.nrc.cadc.dali.tables.votable.VOTableDocument; +import ca.nrc.cadc.reg.Standards; +import ca.nrc.cadc.util.FileUtil; +import ca.nrc.cadc.util.Log4jInit; +import java.io.File; +import java.net.URI; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.Assert; + +/** + * + * @author pdowler + */ +public class DapQueryGetTest extends SyncUWSTest { + + private static final Logger log = Logger.getLogger(DapQueryGetTest.class); + + static { + Log4jInit.setLevel("org.opencadc.dap", Level.INFO); + Log4jInit.setLevel("ca.nrc.cadc.conformance.uws2", Level.INFO); + } + + public DapQueryGetTest() { + super(Constants.DAP_RESOURCE_ID, Standards.DAP_QUERY_21); + + File testFile = FileUtil.getFileFromResource("SyncTest-OK-BAND.properties", DapQueryGetTest.class); + if (testFile.exists()) { + File testDir = testFile.getParentFile(); + super.setPropertiesDir(testDir, "SyncTest-OK"); + } + } + + @Override + protected void validateResponse(JobResultWrapper result) { + Assert.assertEquals(200, result.responseCode); + Assert.assertEquals("application/x-votable+xml", result.contentType); + + try { + Assert.assertNotNull(result.syncOutput); + VOTableDocument vot = VOTableHandler.getVOTable(result.syncOutput); + log.info(result.name + ": found valid VOTable"); + + String queryStatus = VOTableHandler.getQueryStatus(vot); + Assert.assertNotNull("QUERY_STATUS", queryStatus); + Assert.assertEquals("OK", queryStatus); + } catch (Exception ex) { + log.error("unexpected exception", ex); + Assert.fail("unexpected exception: " + ex); + } + } +} diff --git a/dap/src/intTest/java/org/opencadc/dap/SiaQuery2ErrorTest.java b/dap/src/intTest/java/org/opencadc/dap/SiaQuery2ErrorTest.java index 08f4f959..53b48a7d 100644 --- a/dap/src/intTest/java/org/opencadc/dap/SiaQuery2ErrorTest.java +++ b/dap/src/intTest/java/org/opencadc/dap/SiaQuery2ErrorTest.java @@ -90,12 +90,12 @@ public class SiaQuery2ErrorTest extends SyncUWSTest { private static final Logger log = Logger.getLogger(SiaQuery2ErrorTest.class); static { - Log4jInit.setLevel("ca.nrc.cadc.sia", Level.INFO); + Log4jInit.setLevel("org.opencadc.dap", Level.INFO); Log4jInit.setLevel("ca.nrc.cadc.conformance.uws2", Level.INFO); } public SiaQuery2ErrorTest() { - super(URI.create("ivo://opencadc.org/sia2"), Standards.SIA_QUERY_20); + super(Constants.SIA_RESOURCE_ID, Standards.SIA_QUERY_20); File testFile = FileUtil.getFileFromResource("SyncTest-ERROR-BAND.properties", SiaQuery2ErrorTest.class); if (testFile.exists()) { diff --git a/dap/src/intTest/java/org/opencadc/dap/SiaQuery2GetTest.java b/dap/src/intTest/java/org/opencadc/dap/SiaQuery2GetTest.java index ddb2f2ad..9ae75116 100644 --- a/dap/src/intTest/java/org/opencadc/dap/SiaQuery2GetTest.java +++ b/dap/src/intTest/java/org/opencadc/dap/SiaQuery2GetTest.java @@ -90,12 +90,12 @@ public class SiaQuery2GetTest extends SyncUWSTest { private static final Logger log = Logger.getLogger(SiaQuery2GetTest.class); static { - Log4jInit.setLevel("ca.nrc.cadc.sia", Level.INFO); + Log4jInit.setLevel("org.opencadc.dap", Level.INFO); Log4jInit.setLevel("ca.nrc.cadc.conformance.uws2", Level.INFO); } public SiaQuery2GetTest() { - super(URI.create("ivo://opencadc.org/sia2"), Standards.SIA_QUERY_20); + super(Constants.SIA_RESOURCE_ID, Standards.SIA_QUERY_20); File testFile = FileUtil.getFileFromResource("SyncTest-OK-BAND.properties", SiaQuery2GetTest.class); if (testFile.exists()) { diff --git a/dap/src/intTest/java/org/opencadc/dap/VosiAvailabilityTest.java b/dap/src/intTest/java/org/opencadc/dap/VosiAvailabilityTest.java index c03fa7b9..50c48570 100644 --- a/dap/src/intTest/java/org/opencadc/dap/VosiAvailabilityTest.java +++ b/dap/src/intTest/java/org/opencadc/dap/VosiAvailabilityTest.java @@ -3,7 +3,7 @@ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * -* (c) 2023. (c) 2023. +* (c) 2024. (c) 2024. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 @@ -70,14 +70,9 @@ package org.opencadc.dap; import ca.nrc.cadc.vosi.AvailabilityTest; -import java.net.URI; -import org.apache.log4j.Logger; public class VosiAvailabilityTest extends AvailabilityTest { - - private static final Logger log = Logger.getLogger(VosiAvailabilityTest.class); - public VosiAvailabilityTest() { - super(URI.create("ivo://opencadc.org/sia2")); + super(Constants.DAP_RESOURCE_ID); } } diff --git a/dap/src/intTest/java/org/opencadc/dap/VosiCapabilitiesTest.java b/dap/src/intTest/java/org/opencadc/dap/VosiCapabilitiesTest.java index f1f8ce11..ac23b9ec 100644 --- a/dap/src/intTest/java/org/opencadc/dap/VosiCapabilitiesTest.java +++ b/dap/src/intTest/java/org/opencadc/dap/VosiCapabilitiesTest.java @@ -3,7 +3,7 @@ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * -* (c) 2023. (c) 2023. +* (c) 2024. (c) 2024. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 @@ -70,14 +70,9 @@ package org.opencadc.dap; import ca.nrc.cadc.vosi.CapabilitiesTest; -import java.net.URI; -import org.apache.log4j.Logger; public class VosiCapabilitiesTest extends CapabilitiesTest { - - private static final Logger log = Logger.getLogger(VosiCapabilitiesTest.class); - public VosiCapabilitiesTest() { - super(URI.create("ivo://opencadc.org/sia2")); + super(Constants.DAP_RESOURCE_ID); } } diff --git a/dap/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties b/dap/src/intTest/resources/disable-SyncTest-ERROR-DPTYPE.properties similarity index 100% rename from dap/src/intTest/resources/SyncTest-ERROR-DPTYPE.properties rename to dap/src/intTest/resources/disable-SyncTest-ERROR-DPTYPE.properties diff --git a/dap/src/main/java/org/opencadc/dap/AdqlQueryGenerator.java b/dap/src/main/java/org/opencadc/dap/AdqlQueryGenerator.java index 7340dac7..a5376651 100644 --- a/dap/src/main/java/org/opencadc/dap/AdqlQueryGenerator.java +++ b/dap/src/main/java/org/opencadc/dap/AdqlQueryGenerator.java @@ -125,16 +125,26 @@ protected String getQuery() { StringBuilder query = new StringBuilder(); query.append("SELECT * FROM "); query.append(tableName); - query.append(" WHERE dataproduct_type IN ( 'image', 'cube' )"); - DapParamValidator sia = new DapParamValidator(sia2mode); - List pos = sia.validatePOS(queryParams); + DapParamValidator dapParamValidator = new DapParamValidator(sia2mode); + final List pos = dapParamValidator.validatePOS(queryParams); + final List bands = dapParamValidator.validateBAND(queryParams); + final List times = dapParamValidator.validateTIME(queryParams); + final List pols = dapParamValidator.validatePOL(queryParams); + + if (sia2mode) { + query.append(" WHERE dataproduct_type IN ( 'image', 'cube' )"); + } else { + // subsequent append to query is much simpler if there is already a + // where clause and everything else is AND ... + query.append(" WHERE dataproduct_type IS NOT NULL"); + } + if (!pos.isEmpty()) { boolean needOr = false; + query.append(" AND "); if (pos.size() > 1) { - query.append(" AND ("); - } else { - query.append(" AND "); + query.append("("); } for (Shape s : pos) { if (needOr) { @@ -201,13 +211,10 @@ protected String getQuery() { } } - List bands = sia.validateBAND(queryParams); addNumericRangeConstraint(query, "em_min", "em_max", bands); - - List times = sia.validateTIME(queryParams); + addNumericRangeConstraint(query, "t_min", "t_max", times); - - List pols = sia.validatePOL(queryParams); + if (!pols.isEmpty()) { // for a single pattern-matching LIKE statement, we need to sort the POL values in canoncial order // and stick in wildcard % whenever there is a gap @@ -218,10 +225,9 @@ protected String getQuery() { // polStates.add( PolarizationState.valueOf(p)); //} + query.append(" AND "); if (pols.size() > 1) { - query.append(" AND ("); - } else { - query.append(" AND "); + query.append(" ("); } boolean needOr = false; for (PolarizationState p : pols) { @@ -238,43 +244,43 @@ protected String getQuery() { } } - List fovs = sia.validateFOV(queryParams); + List fovs = dapParamValidator.validateFOV(queryParams); addNumericRangeConstraint(query, "s_fov", "s_fov", fovs); - List ress = sia.validateSPATRES(queryParams); + List ress = dapParamValidator.validateSPATRES(queryParams); addNumericRangeConstraint(query, "s_resolution", "s_resolution", ress); - List exptimes = sia.validateEXPTIME(queryParams); + List exptimes = dapParamValidator.validateEXPTIME(queryParams); addNumericRangeConstraint(query, "t_exptime", "t_exptime", exptimes); - List ids = sia.validateID(queryParams); + List ids = dapParamValidator.validateID(queryParams); addStringListConstraint(query, "obs_publisher_did", ids); - List collections = sia.validateCOLLECTION(queryParams); + List collections = dapParamValidator.validateCOLLECTION(queryParams); addStringListConstraint(query, "obs_collection", collections); - List facilities = sia.validateFACILITY(queryParams); + List facilities = dapParamValidator.validateFACILITY(queryParams); addStringListConstraint(query, "facility_name", facilities); - List instruments = sia.validateINSTRUMENT(queryParams); + List instruments = dapParamValidator.validateINSTRUMENT(queryParams); addStringListConstraint(query, "instrument_name", instruments); - List calibs = sia.validateCALIB(queryParams); + List calibs = dapParamValidator.validateCALIB(queryParams); addIntegerListConstraint(query, "calib_level", calibs); - List targets = sia.validateTARGET(queryParams); + List targets = dapParamValidator.validateTARGET(queryParams); addStringListConstraint(query, "target_name", targets); - List timeress = sia.validateTIMERES(queryParams); + List timeress = dapParamValidator.validateTIMERES(queryParams); addNumericRangeConstraint(query, "t_resolution", "t_resolution", timeress); - List specrps = sia.validateSPECRP(queryParams); + List specrps = dapParamValidator.validateSPECRP(queryParams); addNumericRangeConstraint(query, "em_res_power", "em_res_power", specrps); - List formats = sia.validateFORMAT(queryParams); + List formats = dapParamValidator.validateFORMAT(queryParams); addStringListConstraint(query, "access_format", formats); - List dptypes = sia.validateDPTYPE(queryParams); + List dptypes = dapParamValidator.validateDPTYPE(queryParams); addStringListConstraint(query, "dataproduct_type", dptypes); return query.toString(); diff --git a/dap/src/main/java/org/opencadc/dap/DapConfig.java b/dap/src/main/java/org/opencadc/dap/DapConfig.java index c745c158..969fe1d6 100644 --- a/dap/src/main/java/org/opencadc/dap/DapConfig.java +++ b/dap/src/main/java/org/opencadc/dap/DapConfig.java @@ -93,9 +93,11 @@ public class DapConfig { private static final String BASE_KEY = "org.opencadc.dap"; private static final String QUERY_KEY = BASE_KEY + ".queryService"; + private static final String SIA2_KEY = BASE_KEY + ".sia2mode"; private static final String TABLE_KEY = BASE_KEY + ".table"; private final URI queryService; + private final boolean sia2mode; private final String tableName; public DapConfig() { @@ -123,6 +125,9 @@ public DapConfig() { } this.queryService = qsURI; + String s2m = props.getFirstPropertyValue(SIA2_KEY); + this.sia2mode = "true".equals(s2m); + String tn = props.getFirstPropertyValue(TABLE_KEY); if (tn == null) { this.tableName = STD_OBSCORE_TABLE; @@ -134,6 +139,10 @@ public DapConfig() { } } + public boolean isSia2mode() { + return sia2mode; + } + public String getTableName() { return tableName; } diff --git a/dap/src/main/java/org/opencadc/dap/DapInitAction.java b/dap/src/main/java/org/opencadc/dap/DapInitAction.java new file mode 100644 index 00000000..3b3c82b1 --- /dev/null +++ b/dap/src/main/java/org/opencadc/dap/DapInitAction.java @@ -0,0 +1,114 @@ +/* +************************************************************************ +******************* CANADIAN ASTRONOMY DATA CENTRE ******************* +************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** +* +* (c) 2022. (c) 2022. +* Government of Canada Gouvernement du Canada +* National Research Council Conseil national de recherches +* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 +* All rights reserved Tous droits réservés +* +* NRC disclaims any warranties, Le CNRC dénie toute garantie +* expressed, implied, or énoncée, implicite ou légale, +* statutory, of any kind with de quelque nature que ce +* respect to the software, soit, concernant le logiciel, +* including without limitation y compris sans restriction +* any warranty of merchantability toute garantie de valeur +* or fitness for a particular marchande ou de pertinence +* purpose. NRC shall not be pour un usage particulier. +* liable in any event for any Le CNRC ne pourra en aucun cas +* damages, whether direct or être tenu responsable de tout +* indirect, special or general, dommage, direct ou indirect, +* consequential or incidental, particulier ou général, +* arising from the use of the accessoire ou fortuit, résultant +* software. Neither the name de l'utilisation du logiciel. Ni +* of the National Research le nom du Conseil National de +* Council of Canada nor the Recherches du Canada ni les noms +* names of its contributors may de ses participants ne peuvent +* be used to endorse or promote être utilisés pour approuver ou +* products derived from this promouvoir les produits dérivés +* software without specific prior de ce logiciel sans autorisation +* written permission. préalable et particulière +* par écrit. +* +* This file is part of the Ce fichier fait partie du projet +* OpenCADC project. OpenCADC. +* +* OpenCADC is free software: OpenCADC est un logiciel libre ; +* you can redistribute it and/or vous pouvez le redistribuer ou le +* modify it under the terms of modifier suivant les termes de +* the GNU Affero General Public la “GNU Affero General Public +* License as published by the License” telle que publiée +* Free Software Foundation, par la Free Software Foundation +* either version 3 of the : soit la version 3 de cette +* License, or (at your option) licence, soit (à votre gré) +* any later version. toute version ultérieure. +* +* OpenCADC is distributed in the OpenCADC est distribué +* hope that it will be useful, dans l’espoir qu’il vous +* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE +* without even the implied GARANTIE : sans même la garantie +* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ +* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF +* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence +* General Public License for Générale Publique GNU Affero +* more details. pour plus de détails. +* +* You should have received Vous devriez avoir reçu une +* a copy of the GNU Affero copie de la Licence Générale +* General Public License along Publique GNU Affero avec +* with OpenCADC. If not, see OpenCADC ; si ce n’est +* . pas le cas, consultez : +* . +* +************************************************************************ +*/ + +package org.opencadc.dap; + +import ca.nrc.cadc.db.DBUtil; +import ca.nrc.cadc.rest.InitAction; +import ca.nrc.cadc.uws.server.impl.InitDatabaseUWS; +import java.net.URI; +import javax.sql.DataSource; +import org.apache.log4j.Logger; + +/** + * + * @author pdowler + */ +public class DapInitAction extends InitAction { + private static final Logger log = Logger.getLogger(DapInitAction.class); + + private static final URI STD_DAP = URI.create("ivo://ivoa.net/std/DAP#query-2.1"); + private static final URI STD_SIA = URI.create("ivo://ivoa.net/std/SIA#query-2.0"); + private static final String CAP_PROPERTY = "org.opencadc.dap.standardID"; + + public DapInitAction() { + } + + @Override + public void doInit() { + // fix standardID in capabilities before CapInitAction loads + DapConfig dc = new DapConfig(); + if (dc.isSia2mode()) { + System.setProperty(CAP_PROPERTY, STD_SIA.toASCIIString()); + } else { + System.setProperty(CAP_PROPERTY, STD_DAP.toASCIIString()); + } + log.info("standardID: " + System.getProperty(CAP_PROPERTY)); + + try { + log.info("InitDatabaseUWS"); + DataSource uws = DBUtil.findJNDIDataSource("jdbc/uws"); + InitDatabaseUWS uwsi = new InitDatabaseUWS(uws, null, "uws"); + uwsi.doInit(); + log.info("InitDatabaseUWS: OK"); + } catch (Exception ex) { + throw new RuntimeException("INIT FAIL", ex); + } + } + + +} diff --git a/dap/src/main/java/org/opencadc/dap/DapQueryRunner.java b/dap/src/main/java/org/opencadc/dap/DapQueryRunner.java index 62f79f24..627072ad 100644 --- a/dap/src/main/java/org/opencadc/dap/DapQueryRunner.java +++ b/dap/src/main/java/org/opencadc/dap/DapQueryRunner.java @@ -109,8 +109,6 @@ public class DapQueryRunner implements JobRunner { private static final Integer DEF_MAXREC = 1000; private static final Integer MAX_MAXREC = null; - private final boolean sia2mode = true; - private Job job; private JobUpdater jobUpdater; private SyncOutput syncOutput; @@ -167,7 +165,7 @@ private void doit() { Map> queryParams = pe.getParameters(job.getParameterList()); // Get the ADQL request parameters. - AdqlQueryGenerator queryGenerator = new AdqlQueryGenerator(queryParams, conf.getTableName(), sia2mode); + AdqlQueryGenerator queryGenerator = new AdqlQueryGenerator(queryParams, conf.getTableName(), conf.isSia2mode()); Map parameters = queryGenerator.getParameterMap(); parameters.put("RESPONSEFORMAT", VOTableWriter.CONTENT_TYPE); if (maxrec != null) { diff --git a/dap/src/main/webapp/META-INF/context.xml b/dap/src/main/webapp/META-INF/context.xml index 701e7941..ce768e8d 100644 --- a/dap/src/main/webapp/META-INF/context.xml +++ b/dap/src/main/webapp/META-INF/context.xml @@ -9,9 +9,9 @@ factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" closeMethod="close" minEvictableIdleTimeMillis="60000" timeBetweenEvictionRunsMillis="30000" maxWait="20000" - initialSize="0" minIdle="0" maxIdle="${org.opencadc.sia2.uws.maxActive}" maxActive="${org.opencadc.sia2.uws.maxActive}" - username="${org.opencadc.sia2.uws.username}" password="${org.opencadc.sia2.uws.password}" - driverClassName="org.postgresql.Driver" url="${org.opencadc.sia2.uws.url}" + initialSize="0" minIdle="0" maxIdle="${org.opencadc.dap.uws.maxActive}" maxActive="${org.opencadc.dap.uws.maxActive}" + username="${org.opencadc.dap.uws.username}" password="${org.opencadc.dap.uws.password}" + driverClassName="org.postgresql.Driver" url="${org.opencadc.dap.uws.url}" removeAbandoned="false" testOnBorrow="true" validationQuery="select 123" /> diff --git a/dap/src/main/webapp/WEB-INF/web.xml b/dap/src/main/webapp/WEB-INF/web.xml index 9ff1ed5b..6f065247 100644 --- a/dap/src/main/webapp/WEB-INF/web.xml +++ b/dap/src/main/webapp/WEB-INF/web.xml @@ -26,6 +26,7 @@ org.opencadc.dap ca.nrc.cadc.dali ca.nrc.cadc.uws + ca.nrc.cadc.reg ca.nrc.cadc.rest ca.nrc.cadc.vosi ca.nrc.cadc.auth @@ -36,12 +37,12 @@ - 3 - SiaServlet + 2 + QueryServlet ca.nrc.cadc.uws.server.JobServlet init - org.opencadc.sia2.UWSInitAction + org.opencadc.dap.DapInitAction get @@ -63,18 +64,6 @@ 3 - AvailabilityServlet - ca.nrc.cadc.vosi.AvailabilityServlet - - - - ca.nrc.cadc.vosi.AvailabilityPlugin - org.opencadc.dap.ServiceAvailability - - - - - 2 CapabilitiesServlet ca.nrc.cadc.rest.RestServlet @@ -94,9 +83,21 @@ /capabilities.xml + + + 4 + AvailabilityServlet + ca.nrc.cadc.vosi.AvailabilityServlet + + + + ca.nrc.cadc.vosi.AvailabilityPlugin + org.opencadc.dap.ServiceAvailability + + - SiaServlet + QueryServlet /query/* diff --git a/dap/src/main/webapp/capabilities.xml b/dap/src/main/webapp/capabilities.xml index 7d56953c..83f0041f 100644 --- a/dap/src/main/webapp/capabilities.xml +++ b/dap/src/main/webapp/capabilities.xml @@ -25,8 +25,7 @@ - - + https://replace.me.com/dap/query diff --git a/dap/src/test/java/org/opencadc/dap/AdqlQueryGeneratorTest.java b/dap/src/test/java/org/opencadc/dap/AdqlQueryGeneratorTest.java index ef087254..595193d0 100644 --- a/dap/src/test/java/org/opencadc/dap/AdqlQueryGeneratorTest.java +++ b/dap/src/test/java/org/opencadc/dap/AdqlQueryGeneratorTest.java @@ -292,7 +292,6 @@ public void testAltTableNames() { try { Map> params = new TreeMap>(new CaseInsensitiveStringComparator()); - params.put("POS", Arrays.asList("RANGE 0 360 -2 2", "RANGE 10 20 -90 90", "RANGE 1 2 3 4")); AdqlQueryGenerator gen = new AdqlQueryGenerator(params, "schema.TableName", sia2mode); String adql = gen.getQuery(); @@ -300,8 +299,6 @@ public void testAltTableNames() { String selectStmt = "SELECT * FROM schema.TableName WHERE"; Assert.assertTrue("schema.TableName", adql.contains(selectStmt)); - Assert.assertTrue("dataproduct_type", adql.contains("dataproduct_type")); - Assert.assertTrue("s_region", adql.contains("s_region")); } catch (Exception unexpected) { log.error("unexpected exception", unexpected); Assert.fail("unexpected exception: " + unexpected); From 21c7e131d9b94a5bd932346e2d3445cc319ab509 Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Tue, 8 Oct 2024 15:19:27 -0700 Subject: [PATCH 09/11] revert caom2 polygon code to release dap --- .../cadc/dali/InvalidPolygonException.java | 80 ----- .../main/java/ca/nrc/cadc/dali/Polygon.java | 330 +----------------- .../java/ca/nrc/cadc/dali/PolygonTest.java | 243 ------------- 3 files changed, 3 insertions(+), 650 deletions(-) delete mode 100644 cadc-dali/src/main/java/ca/nrc/cadc/dali/InvalidPolygonException.java delete mode 100644 cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java diff --git a/cadc-dali/src/main/java/ca/nrc/cadc/dali/InvalidPolygonException.java b/cadc-dali/src/main/java/ca/nrc/cadc/dali/InvalidPolygonException.java deleted file mode 100644 index 21246a09..00000000 --- a/cadc-dali/src/main/java/ca/nrc/cadc/dali/InvalidPolygonException.java +++ /dev/null @@ -1,80 +0,0 @@ -/* -************************************************************************ -******************* CANADIAN ASTRONOMY DATA CENTRE ******************* -************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** -* -* (c) 2023. (c) 2023. -* Government of Canada Gouvernement du Canada -* National Research Council Conseil national de recherches -* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 -* All rights reserved Tous droits réservés -* -* NRC disclaims any warranties, Le CNRC dénie toute garantie -* expressed, implied, or énoncée, implicite ou légale, -* statutory, of any kind with de quelque nature que ce -* respect to the software, soit, concernant le logiciel, -* including without limitation y compris sans restriction -* any warranty of merchantability toute garantie de valeur -* or fitness for a particular marchande ou de pertinence -* purpose. NRC shall not be pour un usage particulier. -* liable in any event for any Le CNRC ne pourra en aucun cas -* damages, whether direct or être tenu responsable de tout -* indirect, special or general, dommage, direct ou indirect, -* consequential or incidental, particulier ou général, -* arising from the use of the accessoire ou fortuit, résultant -* software. Neither the name de l'utilisation du logiciel. Ni -* of the National Research le nom du Conseil National de -* Council of Canada nor the Recherches du Canada ni les noms -* names of its contributors may de ses participants ne peuvent -* be used to endorse or promote être utilisés pour approuver ou -* products derived from this promouvoir les produits dérivés -* software without specific prior de ce logiciel sans autorisation -* written permission. préalable et particulière -* par écrit. -* -* This file is part of the Ce fichier fait partie du projet -* OpenCADC project. OpenCADC. -* -* OpenCADC is free software: OpenCADC est un logiciel libre ; -* you can redistribute it and/or vous pouvez le redistribuer ou le -* modify it under the terms of modifier suivant les termes de -* the GNU Affero General Public la “GNU Affero General Public -* License as published by the License” telle que publiée -* Free Software Foundation, par la Free Software Foundation -* either version 3 of the : soit la version 3 de cette -* License, or (at your option) licence, soit (à votre gré) -* any later version. toute version ultérieure. -* -* OpenCADC is distributed in the OpenCADC est distribué -* hope that it will be useful, dans l’espoir qu’il vous -* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE -* without even the implied GARANTIE : sans même la garantie -* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ -* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF -* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence -* General Public License for Générale Publique GNU Affero -* more details. pour plus de détails. -* -* You should have received Vous devriez avoir reçu une -* a copy of the GNU Affero copie de la Licence Générale -* General Public License along Publique GNU Affero avec -* with OpenCADC. If not, see OpenCADC ; si ce n’est -* . pas le cas, consultez : -* . -* -************************************************************************ -*/ - -package ca.nrc.cadc.dali; - -/** - * Exception for a polygon that is invalid according to the DALI specification. - * - * @author pdowler - */ -public class InvalidPolygonException extends Exception { - - public InvalidPolygonException(String msg) { - super(msg); - } -} diff --git a/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java b/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java index 929bea0d..36e8e0f0 100644 --- a/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java +++ b/cadc-dali/src/main/java/ca/nrc/cadc/dali/Polygon.java @@ -3,7 +3,7 @@ ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * -* (c) 2024. (c) 2024. +* (c) 2019. (c) 2019. * Government of Canada Gouvernement du Canada * National Research Council Conseil national de recherches * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 @@ -69,30 +69,15 @@ package ca.nrc.cadc.dali; -import ca.nrc.cadc.dali.impl.CartesianTransform; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import org.apache.log4j.Logger; /** - * DALI polygon class with port of CAOM-2.4 polygon validation code. Note: the code - * in this class assumes that polygons are small (flat) enough that cartesian math is - * a reasonable approximation and it will give wrong answers for polygons that are too - * large (10s of degrees across??) and especially for those larger than half the sphere. - * + * * @author pdowler */ public class Polygon implements Shape { - private static final Logger log = Logger.getLogger(Polygon.class); - - private final List vertices = new ArrayList<>(); - - // lazily computed - private transient Point center; - private transient Double area; - private transient Circle minimumSpanningCircle; - private transient Boolean ccw; + private List vertices = new ArrayList(); public Polygon() { } @@ -132,313 +117,4 @@ public boolean equals(Object obj) { return true; } - /** - * Validate this polygon for conformance to IVOA DALI polygon rules. - * - * @throws InvalidPolygonException violation of DALI spec - */ - public final void validate() throws InvalidPolygonException { - validateSegments(); - initProps(); - // DALI polygons are always "inside on the left" aka CCW - // unsupported: if we detect CW here it is equivalent to the region - // outside with area = 4*pi - area and larger than half the sphere - if (!ccw) { - throw new InvalidPolygonException("clockwise winding direction"); - } - } - - /** - * Check if the polygon is counter-clockwise. - * - * @return true if counter-clockwise (valid), false is clockwise (invalid) - */ - public boolean getCounterClockwise() { - if (ccw == null) { - initProps(); - } - return ccw; - } - - public Point getCenter() { - if (center == null) { - initProps(); - } - return center; - } - - public double getArea() { - if (area == null) { - initProps(); - } - return area; - } - - public double getSize() { - if (minimumSpanningCircle == null) { - initProps(); - } - return 2.0 * minimumSpanningCircle.getRadius(); - } - - public Circle getMinimumSpanningCircle() { - if (minimumSpanningCircle == null) { - initProps(); - } - return minimumSpanningCircle; - } - - private void initProps() { - PolygonProperties pp = computePolygonProperties(); - this.area = pp.area; - this.center = pp.center; - this.minimumSpanningCircle = pp.minSpanCircle; - this.ccw = pp.windCounterClockwise; - } - - private static class PolygonProperties { - boolean windCounterClockwise; - Double area; - Point center; - Circle minSpanCircle; - } - - private PolygonProperties computePolygonProperties() { - // log.debug("computePolygonProperties: " + poly); - // the transform needed for computing things in long/lat using cartesian - // approximation - CartesianTransform trans = CartesianTransform.getTransform(this); - Polygon tpoly = trans.transform(this); - - // algorithm from - // http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/ - double a = 0.0; - double cx = 0.0; - double cy = 0.0; - Iterator pi = tpoly.getVertices().iterator(); - Point start = pi.next(); - Point v1 = start; - while (pi.hasNext()) { - Point v2 = pi.next(); - double tmp = v1.getLongitude() * v2.getLatitude() - v2.getLongitude() * v1.getLatitude(); - a += tmp; - cx += (v1.getLongitude() + v2.getLongitude()) * tmp; - cy += (v1.getLatitude() + v2.getLatitude()) * tmp; - v1 = v2; - if (!pi.hasNext()) { - v2 = start; - tmp = v1.getLongitude() * v2.getLatitude() - v2.getLongitude() * v1.getLatitude(); - a += tmp; - cx += (v1.getLongitude() + v2.getLongitude()) * tmp; - cy += (v1.getLatitude() + v2.getLatitude()) * tmp; - } - } - - a *= 0.5; - cx = cx / (6.0 * a); - cy = cy / (6.0 * a); - log.debug("props: " + cx + "," + cy + " a=" + a); - - - // quick and dirty minimum spanning circle computation - double d = 0.0; - Point e1 = null; - Point e2 = null; - for (int i = 0; i < tpoly.getVertices().size(); i++) { - Point vi = tpoly.getVertices().get(i); - for (int j = i + 1; j < tpoly.getVertices().size(); j++) { - Point vj = tpoly.getVertices().get(j); - double d1 = vi.getLongitude() - vj.getLongitude(); - double d2 = vi.getLatitude() - vj.getLatitude(); - double dd = Math.sqrt(d1 * d1 + d2 * d2); - if (dd > d) { - d = dd; - e1 = vi; - e2 = vj; - } - } - } - - PolygonProperties ret = new PolygonProperties(); - ret.windCounterClockwise = (a < 0.0); // RA-DEC increases left-up - if (a < 0.0) { - a *= -1.0; - } - ret.area = a; - - CartesianTransform inv = trans.getInverseTransform(); - //log.warn("transform: " + cx + "," + cy + " with " + inv); - ret.center = inv.transform(new Point(cx, cy)); - - // midpoint between vertices - if (e1 != null && e2 != null && d > 0.0) { - Point cen = new Point(0.5 * Math.abs(e1.getLongitude() + e2.getLongitude()), - 0.5 * Math.abs(e1.getLatitude() + e2.getLatitude())); - Point mscc = inv.transform(cen); - ret.minSpanCircle = new Circle(mscc, d / 2.0); - } - - return ret; - } - - private void validateSegments() throws InvalidPolygonException { - CartesianTransform trans = CartesianTransform.getTransform(this); - Polygon tpoly = trans.transform(this); - - Iterator vi = tpoly.getVertices().iterator(); - List tsegs = new ArrayList<>(); - List psegs = tsegs; - Point start = vi.next(); - Point v1 = start; - while (vi.hasNext()) { - Point v2 = vi.next(); - Segment s = new Segment(v1, v2); - //log.warn("[validateSegments] tseg: " + s); - tsegs.add(s); - v1 = v2; - if (!vi.hasNext()) { - v2 = start; - s = new Segment(v1, v2); - //log.warn("[validateSegments] implicit tseg: " + s); - tsegs.add(s); - } - } - if (this != tpoly) { - // make segments with orig coords for reporting - vi = this.getVertices().iterator(); - psegs = new ArrayList<>(); - start = vi.next(); - v1 = start; - while (vi.hasNext()) { - Point v2 = vi.next(); - Segment s = new Segment(v1, v2); - //log.warn("[validateSegments] pseg: " + s); - psegs.add(s); - v1 = v2; - if (!vi.hasNext()) { - v2 = start; - s = new Segment(v1, v2); - //log.warn("[validateSegments] implicit pseg: " + s); - psegs.add(s); - } - - } - } - intersects(tsegs, psegs); - } - - private static void intersects(List transSegments, List origSegments) throws InvalidPolygonException { - for (int i = 0; i < transSegments.size(); i++) { - Segment s1 = transSegments.get(i); - for (int j = 0; j < transSegments.size(); j++) { - if (i != j) { - Segment s2 = transSegments.get(j); - if (intersects(s1, s2)) { - Segment r1 = origSegments.get(i); - Segment r2 = origSegments.get(j); - throw new InvalidPolygonException("invalid Polygon: segment intersect " + r1 + " vs " + r2); - } - } - } - } - } - - private static boolean intersects(Segment ab, Segment cd) { - //log.debug("intersects: " + ab + " vs " + cd); - // rden = (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) - double den = (ab.v2.getLongitude() - ab.v1.getLongitude()) * (cd.v2.getLatitude() - cd.v1.getLatitude()) - - (ab.v2.getLatitude() - ab.v1.getLatitude()) * (cd.v2.getLongitude() - cd.v1.getLongitude()); - //log.debug("den = " + den); - - //rnum = (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy) - double rnum = (ab.v1.getLatitude() - cd.v1.getLatitude()) * (cd.v2.getLongitude() - cd.v1.getLongitude()) - - (ab.v1.getLongitude() - cd.v1.getLongitude()) * (cd.v2.getLatitude() - cd.v1.getLatitude()); - //log.debug("rnum = " + rnum); - - if (Math.abs(den) < 1.0e-12) { //(den == 0.0) - if (Math.abs(rnum) < 1.0e-12) { //(rnum == 0.0) - // colinear: check overlap on one axis - if (ab.v2 == cd.v1 || ab.v1 == cd.v2) { - return false; // end-to-end - } - double len1 = ab.lengthSquared(); - double len2 = cd.lengthSquared(); - Segment s = ab; - if (len2 > len1) { - s = cd; // the longer one - } - double dx = Math.abs(s.v1.getLongitude() - s.v2.getLongitude()); - double dy = Math.abs(s.v1.getLatitude() - s.v2.getLatitude()); - if (dx > dy) { // more horizontal = project to coordX - if (ab.v2.getLongitude() < cd.v1.getLongitude()) { - return false; // ab left of cd - } - if (ab.v1.getLongitude() > cd.v2.getLongitude()) { - return false; // ab right of cd - } - } else { // more vertical = project to coordY - if (ab.v2.getLatitude() < cd.v1.getLatitude()) { - return false; // ab below cd - } - if (ab.v1.getLatitude() > cd.v2.getLatitude()) { - return false; // ab above cd - } - } - return true; // overlapping - } - return false; // just parallel - } - - double r = rnum / den; - //log.debug("radius = " + radius); - // no intersect, =0 or 1 means the ends touch, which is normal but pg_sphere doesn't like it - //if (radius < 0.0 || radius > 1.0) - if (r <= 0.0 || r >= 1.0) { - return false; - } - - //snum = (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) - double snum = (ab.v1.getLatitude() - cd.v1.getLatitude()) * (ab.v2.getLongitude() - ab.v1.getLongitude()) - - (ab.v1.getLongitude() - cd.v1.getLongitude()) * (ab.v2.getLatitude() - ab.v1.getLatitude()); - //log.debug("snum = " + snum); - - double s = snum / den; - //log.debug("s = " + s); - //if (s < 0.0 || s > 1.0) - if (s <= 0.0 || s >= 1.0) { - return false; // no intersect, =0 or 1 means the ends touch, which is normal - } - - // radius in [0,1] and s in [0,1] = intersects - return true; - } - - private static class Segment { - - Point v1; - Point v2; - - Segment(Point v1, Point v2) { - this.v1 = v1; - this.v2 = v2; - } - - double length() { - return Math.sqrt(lengthSquared()); - } - - double lengthSquared() { - return distanceSquared(v1, v2); - } - - @Override - public String toString() { - return "Segment[" + v1.getLongitude() + "," + v1.getLatitude() + ":" + v2.getLongitude() + "," + v2.getLatitude() + "]"; - } - } - - private static double distanceSquared(Point v1, Point v2) { - return (v1.getLongitude() - v2.getLongitude()) * (v1.getLongitude() - v2.getLongitude()) - + (v1.getLatitude() - v2.getLatitude()) * (v1.getLatitude() - v2.getLatitude()); - } } diff --git a/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java b/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java deleted file mode 100644 index bde8b8a8..00000000 --- a/cadc-dali/src/test/java/ca/nrc/cadc/dali/PolygonTest.java +++ /dev/null @@ -1,243 +0,0 @@ -/* -************************************************************************ -******************* CANADIAN ASTRONOMY DATA CENTRE ******************* -************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** -* -* (c) 2023. (c) 2023. -* Government of Canada Gouvernement du Canada -* National Research Council Conseil national de recherches -* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 -* All rights reserved Tous droits réservés -* -* NRC disclaims any warranties, Le CNRC dénie toute garantie -* expressed, implied, or énoncée, implicite ou légale, -* statutory, of any kind with de quelque nature que ce -* respect to the software, soit, concernant le logiciel, -* including without limitation y compris sans restriction -* any warranty of merchantability toute garantie de valeur -* or fitness for a particular marchande ou de pertinence -* purpose. NRC shall not be pour un usage particulier. -* liable in any event for any Le CNRC ne pourra en aucun cas -* damages, whether direct or être tenu responsable de tout -* indirect, special or general, dommage, direct ou indirect, -* consequential or incidental, particulier ou général, -* arising from the use of the accessoire ou fortuit, résultant -* software. Neither the name de l'utilisation du logiciel. Ni -* of the National Research le nom du Conseil National de -* Council of Canada nor the Recherches du Canada ni les noms -* names of its contributors may de ses participants ne peuvent -* be used to endorse or promote être utilisés pour approuver ou -* products derived from this promouvoir les produits dérivés -* software without specific prior de ce logiciel sans autorisation -* written permission. préalable et particulière -* par écrit. -* -* This file is part of the Ce fichier fait partie du projet -* OpenCADC project. OpenCADC. -* -* OpenCADC is free software: OpenCADC est un logiciel libre ; -* you can redistribute it and/or vous pouvez le redistribuer ou le -* modify it under the terms of modifier suivant les termes de -* the GNU Affero General Public la “GNU Affero General Public -* License as published by the License” telle que publiée -* Free Software Foundation, par la Free Software Foundation -* either version 3 of the : soit la version 3 de cette -* License, or (at your option) licence, soit (à votre gré) -* any later version. toute version ultérieure. -* -* OpenCADC is distributed in the OpenCADC est distribué -* hope that it will be useful, dans l’espoir qu’il vous -* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE -* without even the implied GARANTIE : sans même la garantie -* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ -* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF -* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence -* General Public License for Générale Publique GNU Affero -* more details. pour plus de détails. -* -* You should have received Vous devriez avoir reçu une -* a copy of the GNU Affero copie de la Licence Générale -* General Public License along Publique GNU Affero avec -* with OpenCADC. If not, see OpenCADC ; si ce n’est -* . pas le cas, consultez : -* . -* -************************************************************************ - */ - -package ca.nrc.cadc.dali; - -import ca.nrc.cadc.util.Log4jInit; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.junit.Assert; -import org.junit.Test; - -/** - * Port of CAOM-2.4 polygon validation tests. - * - * @author pdowler - */ -public class PolygonTest { - - private static final Logger log = Logger.getLogger(PolygonTest.class); - - static { - Log4jInit.setLevel("ca.nrc.cadc.dali", Level.INFO); - } - - public PolygonTest() { - } - - @Test - public void testValidPolygon() { - try { - Polygon p = new Polygon(); - p.getVertices().add(new Point(2.0, 2.0)); - p.getVertices().add(new Point(1.0, 4.0)); - p.getVertices().add(new Point(3.0, 3.0)); - - p.validate(); - - Assert.assertTrue("CCW", p.getCounterClockwise()); - - Point c = p.getCenter(); - Assert.assertNotNull(c); - Assert.assertEquals(2.0, c.getLongitude(), 0.01); - Assert.assertEquals(3.0, c.getLatitude(), 0.01); - - Assert.assertEquals(1.5, p.getArea(), 0.1); - - Circle msc = p.getMinimumSpanningCircle(); - Assert.assertNotNull(msc); - Assert.assertEquals(1.5, msc.getCenter().getLongitude(), 0.01); - Assert.assertEquals(3.0, msc.getCenter().getLatitude(), 0.01); - Assert.assertEquals(1.12, msc.getRadius(), 0.01); - } catch (Exception unexpected) { - log.error("unexpected exception", unexpected); - Assert.fail("unexpected exception: " + unexpected); - } - } - - @Test - public void testValidPolygonFromFootprintPy() { - // footprint.py output - // old: Polygon ICRS 259.006152 60.047132 259.087308 60.087963 259.089760 60.087730 259.132216 60.068435 259.131770 60.067704 259.133299 60.067895 259.174702 60.049127 259.093400 60.010342 259.078635 60.015734 - String[] oldS = "259.006152 60.047132 259.087308 60.087963 259.089760 60.087730 259.132216 60.068435 259.131770 60.067704 259.133299 60.067895 259.174702 60.049127 259.093400 60.010342 259.078635 60.015734".split(" "); - - // cur: Polygon ICRS 259.006152 60.047132 259.078635 60.015734 259.093400 60.010342 259.174702 60.049127 259.133299 60.067895 259.131770 60.067704 259.132216 60.068435 259.089760 60.087730 259.087308 60.087963 - String[] curS = "259.006152 60.047132 259.078635 60.015734 259.093400 60.010342 259.174702 60.049127 259.133299 60.067895 259.131770 60.067704 259.132216 60.068435 259.089760 60.087730 259.087308 60.087963".split(" "); - - String[] test = oldS; - try { - Polygon p = new Polygon(); - for (int i = 0; i < test.length; i += 2) { - double x = Double.parseDouble(test[i]); - double y = Double.parseDouble(test[i + 1]); - p.getVertices().add(new Point(x, y)); - } - - p.validate(); - - log.info("testValidPolygonFromFootprintPy: " + p); - } catch (Exception unexpected) { - log.error("unexpected exception", unexpected); - Assert.fail("unexpected exception: " + unexpected); - } - } - - @Test - public void testInvalidLongitude() { - try { - Polygon p = new Polygon(); - p.getVertices().add(new Point(360.0, 2.0)); - p.getVertices().add(new Point(359.0, 4.0)); - p.getVertices().add(new Point(361.0, 3.0)); - - p.validate(); - Assert.fail("expected IllegalArgumentException, created: " + p); - } catch (IllegalArgumentException expected) { - log.info("caught expected: " + expected); - Assert.assertTrue(expected.getMessage().startsWith("invalid longitude")); - } catch (Exception unexpected) { - log.error("unexpected exception", unexpected); - Assert.fail("unexpected exception: " + unexpected); - } - } - - @Test - public void testInvalidLatitude() { - try { - Polygon p = new Polygon(); - p.getVertices().add(new Point(2.0, 89.0)); - p.getVertices().add(new Point(1.0, 91.0)); - p.getVertices().add(new Point(3.0, 90.0)); - - p.validate(); - Assert.fail("expected IllegalArgumentException, created: " + p); - } catch (IllegalArgumentException expected) { - log.info("caught expected: " + expected); - Assert.assertTrue(expected.getMessage().startsWith("invalid latitude")); - } catch (Exception unexpected) { - log.error("unexpected exception", unexpected); - Assert.fail("unexpected exception: " + unexpected); - } - } - - @Test - public void testInvalidPolygonCW() { - try { - Polygon p = new Polygon(); - p.getVertices().add(new Point(2.0, 2.0)); - p.getVertices().add(new Point(3.0, 3.0)); - p.getVertices().add(new Point(1.0, 4.0)); - - Assert.assertFalse("CCW", p.getCounterClockwise()); - - p.validate(); - Assert.fail("expected InvalidPolygonException, created: " + p); - } catch (InvalidPolygonException expected) { - log.info("caught expected: " + expected); - } catch (Exception unexpected) { - log.error("unexpected exception", unexpected); - Assert.fail("unexpected exception: " + unexpected); - } - } - - @Test - public void testInvalidPolygonSegmentIntersect() { - try { - Polygon poly = new Polygon(); - poly.getVertices().add(new Point(2.0, 2.0)); - poly.getVertices().add(new Point(2.0, 4.0)); - poly.getVertices().add(new Point(4.0, 2.0)); - poly.getVertices().add(new Point(4.0, 4.0)); - - try { - poly.validate(); - Assert.fail("expected InvalidPolygonException - got: " + poly); - } catch (InvalidPolygonException expected) { - log.info("testValidateSegments: butterfly " + expected); - Assert.assertTrue(expected.getMessage().startsWith("invalid Polygon: segment intersect ")); - } - - poly.getVertices().clear(); - poly.getVertices().add(new Point(2.0, 2.0)); - poly.getVertices().add(new Point(2.0, 4.0)); - poly.getVertices().add(new Point(5.0, 4.0)); // extra small loop - poly.getVertices().add(new Point(4.0, 5.0)); - poly.getVertices().add(new Point(4.0, 2.0)); - - try { - poly.validate(); - Assert.fail("expected InvalidPolygonException - got: " + poly); - } catch (InvalidPolygonException expected) { - log.info("testValidateSegments: small loop " + expected); - Assert.assertTrue(expected.getMessage().contains("invalid Polygon: segment intersect ")); - } - } catch (Exception unexpected) { - log.error("unexpected exception", unexpected); - Assert.fail("unexpected exception: " + unexpected); - } - } -} From 107f96787a9d0f089c9381794b996ee077134800 Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Tue, 8 Oct 2024 15:23:28 -0700 Subject: [PATCH 10/11] remove caom2 polygon related code --- .../cadc/dali/impl/CartesianTransform.java | 355 ------------------ 1 file changed, 355 deletions(-) delete mode 100644 cadc-dali/src/main/java/ca/nrc/cadc/dali/impl/CartesianTransform.java diff --git a/cadc-dali/src/main/java/ca/nrc/cadc/dali/impl/CartesianTransform.java b/cadc-dali/src/main/java/ca/nrc/cadc/dali/impl/CartesianTransform.java deleted file mode 100644 index 755966dd..00000000 --- a/cadc-dali/src/main/java/ca/nrc/cadc/dali/impl/CartesianTransform.java +++ /dev/null @@ -1,355 +0,0 @@ -/* -************************************************************************ -******************* CANADIAN ASTRONOMY DATA CENTRE ******************* -************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** -* -* (c) 2023. (c) 2023. -* Government of Canada Gouvernement du Canada -* National Research Council Conseil national de recherches -* Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 -* All rights reserved Tous droits réservés -* -* NRC disclaims any warranties, Le CNRC dénie toute garantie -* expressed, implied, or énoncée, implicite ou légale, -* statutory, of any kind with de quelque nature que ce -* respect to the software, soit, concernant le logiciel, -* including without limitation y compris sans restriction -* any warranty of merchantability toute garantie de valeur -* or fitness for a particular marchande ou de pertinence -* purpose. NRC shall not be pour un usage particulier. -* liable in any event for any Le CNRC ne pourra en aucun cas -* damages, whether direct or être tenu responsable de tout -* indirect, special or general, dommage, direct ou indirect, -* consequential or incidental, particulier ou général, -* arising from the use of the accessoire ou fortuit, résultant -* software. Neither the name de l'utilisation du logiciel. Ni -* of the National Research le nom du Conseil National de -* Council of Canada nor the Recherches du Canada ni les noms -* names of its contributors may de ses participants ne peuvent -* be used to endorse or promote être utilisés pour approuver ou -* products derived from this promouvoir les produits dérivés -* software without specific prior de ce logiciel sans autorisation -* written permission. préalable et particulière -* par écrit. -* -* This file is part of the Ce fichier fait partie du projet -* OpenCADC project. OpenCADC. -* -* OpenCADC is free software: OpenCADC est un logiciel libre ; -* you can redistribute it and/or vous pouvez le redistribuer ou le -* modify it under the terms of modifier suivant les termes de -* the GNU Affero General Public la “GNU Affero General Public -* License as published by the License” telle que publiée -* Free Software Foundation, par la Free Software Foundation -* either version 3 of the : soit la version 3 de cette -* License, or (at your option) licence, soit (à votre gré) -* any later version. toute version ultérieure. -* -* OpenCADC is distributed in the OpenCADC est distribué -* hope that it will be useful, dans l’espoir qu’il vous -* but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE -* without even the implied GARANTIE : sans même la garantie -* warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ -* or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF -* PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence -* General Public License for Générale Publique GNU Affero -* more details. pour plus de détails. -* -* You should have received Vous devriez avoir reçu une -* a copy of the GNU Affero copie de la Licence Générale -* General Public License along Publique GNU Affero avec -* with OpenCADC. If not, see OpenCADC ; si ce n’est -* . pas le cas, consultez : -* . -* -* $Revision: 5 $ -* -************************************************************************ - */ - -package ca.nrc.cadc.dali.impl; - -import ca.nrc.cadc.dali.Circle; -import ca.nrc.cadc.dali.Point; -import ca.nrc.cadc.dali.Polygon; -import org.apache.log4j.Logger; - -/** - * Utility code to provide a 3D cartesian transform that "rotates" shapes around - * the unit sphere so they are far away from the meridian and poles. - * - * @author pdowler - */ -public class CartesianTransform { - private static final Logger log = Logger.getLogger(CartesianTransform.class); - - // the negative X axis - static final double[] NEG_X_AXIS = new double[] { -1.0, 0.0, 0.0 }; - - // the positive X axis - static final double[] POS_X_AXIS = new double[] { 1.0, 0.0, 0.0 }; - - public static final String X = "x"; - public static final String Y = "y"; - public static final String Z = "z"; - - // some tests mess with these - double angle; - String axis; - - private CartesianTransform() { - } - - public static CartesianTransform getTransform(Polygon poly) { - return getTransform(poly, false); - } - - public static CartesianTransform getTransform(Polygon poly, boolean force) { - double[] cube = getBoundingCube(poly, null); - return getTransform(cube, force); - } - - public static CartesianTransform getTransform(Circle c) { - return getTransform(c, false); - } - - public static CartesianTransform getTransform(Circle c, boolean force) { - double[] xyz = CartesianTransform.toUnitSphere(c.getCenter().getLongitude(), c.getCenter().getLatitude()); - double[] cube = new double[] { - xyz[0], xyz[0], - xyz[1], xyz[1], - xyz[2], xyz[2] - }; - return getTransform(cube, force); - } - - private static CartesianTransform getTransform(double[] cube, boolean force) { - double x1 = cube[0]; - double x2 = cube[1]; - double y1 = cube[2]; - double y2 = cube[3]; - double z1 = cube[4]; - double z2 = cube[5]; - // log.debug("getTransform: bounding cube = " + x1+ ":" + x2 + " " + y1 - // + ":" + y2 + " " + z1 + ":" + z2); - - double cx = 0.5 * (x1 + x2); - double cz = 0.5 * (z1 + z2); - // log.debug("getTransform: bounding cube center = " + cx + " " + cy + " - // " + cz); - - if (Math.abs(cz) > 0.02 || force) { - CartesianTransform trans = new CartesianTransform(); - trans.axis = CartesianTransform.Y; - // project vector in X-Z plane and normalise - double mv1 = Math.sqrt(cx * cx + cz * cz); - double[] v1 = { cx / mv1, 0, cz / mv1 }; - // acos is only valid for 0 to PI - if (cz > 0.0) { // north - // angle to +X axis, then add 180 degrees - double[] v2 = { 1, 0, 0 }; - double cosa = dotProduct(v1, v2); - trans.angle = Math.acos(cosa) + Math.PI; - } else { // south - // directly get angle to -X axis - double[] v2 = { -1, 0, 0 }; - double cosa = dotProduct(v1, v2); - trans.angle = Math.acos(cosa); - } - log.debug("off equator: " + trans); - return trans; - } - if (y1 <= 0.0 && y2 >= 0.0 && x1 > 0.0) { - CartesianTransform trans = new CartesianTransform(); - trans.angle = Math.PI; - trans.axis = CartesianTransform.Z; - log.debug("straddling meridan at equator: " + trans); - return trans; - } - return new CartesianTransform(); // no-op - } - - public CartesianTransform getInverseTransform() { - if (isNull()) { - return this; - } - CartesianTransform ret = new CartesianTransform(); - ret.axis = axis; - ret.angle = -1.0 * angle; - return ret; - } - - @Override - public String toString() { - return "CartesianTransform[" + axis + "," + angle + "]"; - } - - public boolean isNull() { - return axis == null; - } - - /** - * Convert long,lat coordinates to cartesian coordinates on the unit sphere. - * NOTE: this uses a right-handed unit sphere but looking from the outside, - * which is opposite to the normal astro convention of looking from the - * center (which means theta goes in the other direction in astronomy), but - * as long as the opposite conversion is done with toLongLat that is just - * fine. - * - * @param longitude - * @param latitude - * @return a double[3] - */ - public static double[] toUnitSphere(double longitude, double latitude) { - double[] ret = new double[3]; - double theta = Math.toRadians(longitude); - double phi = Math.toRadians(90.0 - latitude); - ret[0] = Math.cos(theta) * Math.sin(phi); - ret[1] = Math.sin(theta) * Math.sin(phi); - ret[2] = Math.cos(phi); - return ret; - } - - /** - * Convert cartesian points on the unit sphere to long,lat. - * - * @param x - * @param y - * @param z - * @return a double[2] - */ - public static double[] toLongLat(double x, double y, double z) { - double[] ret = new double[2]; - if ((x == 0.0 && y == 0.0) || z == 1.0 || z == -1.0) { - ret[0] = 0.0; - } else { - ret[0] = Math.toDegrees(Math.atan2(y, x)); - } - if (ret[0] < 0.0) { - ret[0] += 360.0; - } - if (z > 1.0) { - z = 1.0; - } else if (z < -1.0) { - z = -1.0; - } - ret[1] = 90.0 - Math.toDegrees(Math.acos(z)); - return ret; - } - - public Point transform(Point p) { - if (isNull()) { - return p; - } - - double[] p2 = transformPoint(p); - return new Point(p2[0], p2[1]); - } - - public Polygon transform(Polygon p) { - if (isNull()) { - return p; - } - - Polygon ret = new Polygon(); - for (Point v : p.getVertices()) { - ret.getVertices().add(transform(v)); - } - return ret; - } - - // impl for above methods - private double[] transformPoint(Point p) { - double[] xyz = CartesianTransform.toUnitSphere(p.getLongitude(), p.getLatitude()); - double[] dp = rotate(xyz); - return CartesianTransform.toLongLat(dp[0], dp[1], dp[2]); - } - - public static double[] getBoundingCube(Polygon poly, double[] cube) { - // x1, x2, y1, y2, z1, z2 - if (cube == null) { - cube = new double[6]; - cube[0] = Double.POSITIVE_INFINITY; - cube[1] = Double.NEGATIVE_INFINITY; - cube[2] = Double.POSITIVE_INFINITY; - cube[3] = Double.NEGATIVE_INFINITY; - cube[4] = Double.POSITIVE_INFINITY; - cube[5] = Double.NEGATIVE_INFINITY; - } - for (Point v : poly.getVertices()) { - double[] xyz = CartesianTransform.toUnitSphere(v.getLongitude(), - v.getLatitude()); - cube[0] = Math.min(cube[0], xyz[0]); - cube[1] = Math.max(cube[1], xyz[0]); - - cube[2] = Math.min(cube[2], xyz[1]); - cube[3] = Math.max(cube[3], xyz[1]); - - cube[4] = Math.min(cube[4], xyz[2]); - cube[5] = Math.max(cube[5], xyz[2]); - } - // fix X bounds assuming smallish polygons and cubes - if (cube[2] < 0.0 && 0.0 < cube[3] && cube[4] < 0.0 && 0.0 < cube[5]) { - if (cube[0] > 0.0) { - cube[1] = 1.01; // slightly outside sphere - } else if (cube[1] < 0.0) { - cube[0] = -1.01; // slightly outside sphere - } - } - // fix Y bounds assuming smallish polygons and cubes - if (cube[0] < 0.0 && 0.0 < cube[1] && cube[4] < 0.0 && 0.0 < cube[5]) { - if (cube[2] > 0.0) { - cube[3] = 1.01; // slightly outside sphere - } else if (cube[3] < 0.0) { - cube[2] = -1.01; // slightly outside sphere - } - } - // fix Z bounds assuming smallish polygons and cubes - if (cube[0] < 0.0 && 0.0 < cube[1] && cube[2] < 0.0 && 0.0 < cube[3]) { - if (cube[4] > 0.0) { - cube[5] = 1.01; // slightly outside sphere - } else if (cube[5] < 0.0) { - cube[4] = -1.01; // slightly outside sphere - } - } - return cube; - } - - private static double dotProduct(double[] v1, double[] v2) { - return (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]); - } - - public double[] rotate(double[] p) { - if (Y.equals(axis)) { - return yrotate(p); - } - - if (Z.equals(axis)) { - return zrotate(p); - } - - throw new IllegalStateException("unknown axis: " + axis); - } - - // rotation about the y-axis - private double[] yrotate(double[] p) { - double[] ret = new double[3]; - double cr = Math.cos(angle); - double sr = Math.sin(angle); - ret[0] = cr * p[0] + sr * p[2]; - ret[1] = p[1]; - ret[2] = -1.0 * sr * p[0] + cr * p[2]; - return ret; - } - - // rotation about the z-axis - private double[] zrotate(double[] p) { - double[] ret = new double[3]; - double cr = Math.cos(angle); - double sr = Math.sin(angle); - ret[0] = cr * p[0] + -1 * sr * p[1]; - ret[1] = sr * p[0] + cr * p[1]; - ret[2] = p[2]; - return ret; - } -} From decbf9c0d6b6afcfe641f3cf4302a1dce34aa385 Mon Sep 17 00:00:00 2001 From: Patrick Dowler Date: Tue, 8 Oct 2024 15:46:56 -0700 Subject: [PATCH 11/11] dap: document custom table config --- dap/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dap/README.md b/dap/README.md index e0768ebf..bd69ffa2 100644 --- a/dap/README.md +++ b/dap/README.md @@ -54,6 +54,9 @@ org.opencadc.dap.queryService = {resourceID or TAP base URL} # run in backwards compatible SIAv2 mode (optional) org.opencadc.dap.sia2mode = true | false + +# use a specific ObsCore table in the query service (optional, default: ivoa.ObsCore) +org.opencadc.dap.table = {table name} ``` The _queryService_ is resolved by a registry lookup and that service is used to query for CAOM content. It is assumed that this service is deployed "locally" since there can @@ -61,9 +64,8 @@ be many calls and low latency is very desireable. The _sia2mode_ can be set to make the service behave as an SIA-2.0 service: this causes the generated query to restrict the ObsCore.dataproduct_type values to `cube` and `image`. -TODO: the `/capabilities` endpoint is currently hard-coded to advertise the `SIA#query-2.0` -standardID so that _sia2mode_ is fully correct; as a result "DAP" mode is not really correct -right now. + +The _table_ to query can be set to something other than the default (`ivoa.ObsCore`). `dap` will attempt to use the caller's identity to query, but the details of this depend on the configured IdentityManager and local A&A service configuration.