Skip to content

Commit

Permalink
Merge branch 'Unidata:main' into metno
Browse files Browse the repository at this point in the history
  • Loading branch information
cskarby authored Aug 29, 2023
2 parents 0c81d33 + 0d0b033 commit e6ca3ee
Show file tree
Hide file tree
Showing 36 changed files with 512 additions and 374 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ opendap/src/test/data/test.*.*
tds/src/test/content/thredds/logs
tds/src/test/content/thredds/cache
tds/src/test/content/thredds/state
tds/src/test/content/thredds/notebooks/
tds/src/test/content/thredds/notebooks/default_viewer.ipynb
tds/src/test/content/thredds/templates/
tds/src/test/content/README.txt
tds/src/test/content/thredds/README.txt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Users of OS-provided packages via package management systems for Java and/or Tom
## System Requirements

* OpenJDK Java 11
* Apache Tomcat 8.x (minimum)
* Apache Tomcat 8.x or 9.x

While there are different distributors of Java and servlet containers, Unidata develops, uses and tests the THREDDS Data Server using _OpenJDK Java_ and the _Apache Tomcat_ servlet container.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,13 +361,27 @@ A Notebook configured for all gridded datasets and a dataset called `almostGridd
"viewer_info": {
"description": "This Notebook displays gridded data.",
"accepts": {
"accept_datasetIDs": ["almostGridded],
"accept_datasetIDs": ["almostGridded"],
"accept_dataset_types": ["Grid"]
}
}
}
~~~

The `accept_datasetIDs` can also include regular expressions. This can be useful, for instance, when configuring a
notebook for all datasets in a `datasetScan`:
~~~
"metadata": {
...
"viewer_info": {
"description": "Notebook that displays all datasets in a dataset scan.",
"accepts": {
"accept_datasetIDs": ["myDatasetScanID/.*"],
}
}
}
~~~

#### Suppressing default Notebooks

To suppress default Notebooks, you can override them with a custom Notebook or a dummy Notebook, configured to not accept any datasets.
Expand Down
2 changes: 2 additions & 0 deletions tdcommon/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ dependencies {
testImplementation 'edu.ucar:cdm-test-utils' // Contains stuff like the JUnit @Category classes.
testImplementation 'com.google.truth:truth'
testImplementation 'junit:junit'
testImplementation 'org.mockito:mockito-core'
testRuntimeOnly 'ch.qos.logback:logback-classic'
}

spotless {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package thredds.server.catalog.tracker;

import java.util.Locale;
import net.openhft.chronicle.map.ChronicleMap;
import net.openhft.chronicle.map.ChronicleMapBuilder;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import thredds.client.catalog.Access;
import thredds.client.catalog.Dataset;
Expand All @@ -25,7 +27,18 @@ public class DatasetTrackerChronicle implements DatasetTracker {
static private final String datasetName = "/chronicle.datasets.dat";
// average size (bytes) of key for database, which is the path to a given dataset.
// LOOK: is 512 a good average size? There is no length on file path, so hard to set a maximum.
static private final int averagePathLength = 512;
private static final int averagePathLength = 512;
private final int averageValueSize;

private enum AverageValueSize {
small(200), medium(2_000), large(200_000), defaultSize(small.size);

private final int size;

AverageValueSize(int size) {
this.size = size;
}
}

// delete old databases
public static void cleanupBefore(String pathname, long trackerNumber) {
Expand All @@ -48,9 +61,19 @@ public static void cleanupBefore(String pathname, long trackerNumber) {
private ChronicleMap<String, DatasetExt> datasetMap;

public DatasetTrackerChronicle(String pathname, long maxDatasets, long number) {
this(pathname, maxDatasets, number, AverageValueSize.defaultSize.size);
}

public DatasetTrackerChronicle(String pathname, long maxDatasets, long number, String averageValueSizeName) {
this(pathname, maxDatasets, number, averageValueSizeName == null ? AverageValueSize.defaultSize.size
: AverageValueSize.valueOf(averageValueSizeName.toLowerCase(Locale.ROOT)).size);
}

private DatasetTrackerChronicle(String pathname, long maxDatasets, long number, int averageValueSize) {
dbFile = new File(pathname + datasetName + "." + number);
alreadyExists = dbFile.exists();
this.maxDatasets = maxDatasets;
this.averageValueSize = averageValueSize;

try {
open();
Expand Down Expand Up @@ -107,7 +130,7 @@ public boolean reinit() {

private void open() throws IOException {
ChronicleMapBuilder<String, DatasetExt> builder = ChronicleMapBuilder.of(String.class, DatasetExt.class)
.averageValueSize(200).entries(maxDatasets).averageKeySize(averagePathLength)
.averageValueSize(averageValueSize).entries(maxDatasets).averageKeySize(averagePathLength)
.valueMarshaller(DatasetExtBytesMarshaller.INSTANCE).skipCloseOnExitHook(true);
datasetMap = builder.createPersistedTo(dbFile);
changed = false;
Expand Down Expand Up @@ -178,7 +201,7 @@ else if (!path.equals(accessPath)) { // LOOK must put all for restrict
if (hasNcml) {
// want the ncml string representation
Element ncmlElem = dataset.getNcmlElement();
XMLOutputter xmlOut = new XMLOutputter();
XMLOutputter xmlOut = new XMLOutputter(Format.getCompactFormat());
ncml = xmlOut.outputString(ncmlElem);
}

Expand Down Expand Up @@ -214,4 +237,8 @@ public void showDB(Formatter f) {
}
}

// Package private for testing
long getCount() {
return datasetMap.longSize();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package thredds.server.catalog.tracker;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import thredds.client.catalog.Dataset;

public class TestDatasetTrackerChronicle {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

@Rule
public final TemporaryFolder tempFolder = new TemporaryFolder();

@Test
public void shouldNotTrackDatasetWithoutNcmlAndRestrictions() throws IOException {
try (DatasetTrackerChronicle datasetTracker =
new DatasetTrackerChronicle(tempFolder.getRoot().getAbsolutePath(), 1, 1)) {
assertThat(datasetTracker.getCount()).isEqualTo(0);

final Dataset dataset = Dataset.makeStandalone("path", "featureType", "dataFormat", "serviceType");
datasetTracker.trackDataset(1, dataset, null);
assertThat(datasetTracker.getCount()).isEqualTo(0);
}
}

@Test
public void shouldTrackDatasetWithShortNcml() throws IOException {
try (DatasetTrackerChronicle datasetTracker =
new DatasetTrackerChronicle(tempFolder.getRoot().getAbsolutePath(), 1, 1)) {
assertThat(datasetTracker.getCount()).isEqualTo(0);

datasetTracker.trackDataset(1, mockDataset(100, "path"), null);
assertThat(datasetTracker.getCount()).isEqualTo(1);
}
}

@Test
public void shouldTrackDatasetWithLongNcml() throws IOException {
try (DatasetTrackerChronicle datasetTracker =
new DatasetTrackerChronicle(tempFolder.getRoot().getAbsolutePath(), 1, 1, "Large")) {
assertThat(datasetTracker.getCount()).isEqualTo(0);

datasetTracker.trackDataset(1, mockDataset(10_000, "path"), null);
assertThat(datasetTracker.getCount()).isEqualTo(1);
}
}

@Test
public void shouldTrackMultipleDatasetsWithNcml() throws IOException {
try (DatasetTrackerChronicle datasetTracker =
new DatasetTrackerChronicle(tempFolder.getRoot().getAbsolutePath(), 10, 1, "medium")) {
assertThat(datasetTracker.getCount()).isEqualTo(0);

datasetTracker.trackDataset(1, mockDataset(100, "path1"), null);
datasetTracker.trackDataset(1, mockDataset(1_000, "path2"), null);
datasetTracker.trackDataset(1, mockDataset(10_000, "path3"), null);
assertThat(datasetTracker.getCount()).isEqualTo(3);
}
}

@Test
public void shouldReturnNcml() throws IOException {
try (DatasetTrackerChronicle datasetTracker =
new DatasetTrackerChronicle(tempFolder.getRoot().getAbsolutePath(), 1, 1)) {
datasetTracker.trackDataset(1, mockDataset(100, "path"), null);

final XMLOutputter xmlOut = new XMLOutputter(Format.getCompactFormat());
final String expectedNcml = xmlOut.outputString(createNcml(100));
final String ncml = datasetTracker.findNcml("path");
assertThat(ncml).isEqualTo(expectedNcml);
assertThat(ncml.length()).isEqualTo(139);
}
}

private static Dataset mockDataset(int ncmlLength, String path) {
final Dataset dataset = mock(Dataset.class);
when(dataset.getNcmlElement()).thenReturn(createNcml(ncmlLength));
when(dataset.getUrlPath()).thenReturn(path);
return dataset;
}

private static Element createNcml(int ncmlLength) {
final Element element = new Element("name", "namespace");
element.setAttribute("attribute", "a".repeat(ncmlLength));
return element;
}
}
14 changes: 14 additions & 0 deletions tdcommon/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- Encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default. -->
<encoder>
<!-- See https://logback.qos.ch/manual/layouts.html#ClassicPatternLayout -->
<pattern>%highlight([%d{HH:mm:ss.SSS} %-5level %logger{36}]) %message%n</pattern>
</encoder>
</appender>

<!-- Log all messages at level WARN or higher to STDOUT. -->
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>
4 changes: 2 additions & 2 deletions tds-platform/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies {
// at that point we can take things on a case-by-case basis.
api enforcedPlatform("edu.ucar:netcdf-java-bom:${depVersion.netcdfJava}")
api enforcedPlatform('org.springframework:spring-framework-bom:5.3.27')
api enforcedPlatform('org.springframework.security:spring-security-bom:5.7.8')
api enforcedPlatform('org.springframework.security:spring-security-bom:5.7.10')
api platform('net.openhft:chronicle-bom:2.23.136')
api enforcedPlatform("org.apache.logging.log4j:log4j-bom:2.17.1")
api enforcedPlatform("jakarta.platform:jakarta.jakartaee-bom:8.0.0")
Expand Down Expand Up @@ -82,7 +82,7 @@ dependencies {
// tds
api 'org.json:json:20230227'
api 'com.coverity.security:coverity-escapers:1.1.1'
api 'org.thymeleaf:thymeleaf-spring5:3.0.15.RELEASE'
api 'org.thymeleaf:thymeleaf-spring5:3.1.2.RELEASE'
api 'jakarta.validation:jakarta.validation-api'
api "org.hibernate.validator:hibernate-validator:${depVersion.hibernateValidator}"
api "org.hibernate.validator:hibernate-validator-annotation-processor:${depVersion.hibernateValidator}"
Expand Down
3 changes: 0 additions & 3 deletions tds-ugrid/src/test/resources/cases/fvcom/fvcom_delt.ncml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
<netcdf xmlns="http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2"
location="UCF_FVCOM_DEMO.nc">

<dimension name="face" orgName="nele"/>
<dimension name="node" orgName="node"/>

<!-- X and Y Coordinates -->
<variable name="face_x" orgName="xc">
<attribute name="long_name" value="Characteristics longitude of 2D mesh triangle (e.g. circumcenter coordinate)"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package opendap.test;

import static com.google.common.truth.Truth.assertThat;
import static ucar.ma2.MAMath.nearlyEquals;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.Arrays;
import javax.servlet.http.HttpServletResponse;
import org.junit.Test;
import thredds.test.util.TestOnLocalServer;
import ucar.ma2.Array;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
import ucar.nc2.dataset.NetcdfDatasets;
import ucar.unidata.util.test.TestDir;

public class TestNcmlWithOpendap {
private static final double[] expectedValues = new double[] {Double.NaN, 1, 2, 3};

@Test
public void shouldUseNcmlToModifyDatasetScanValues() throws IOException {
final String path = "/thredds/dodsC/EnhancedDatasetScan/testgrid1.nc";
final String url = "dods://" + TestDir.remoteTestServer + path;
try (NetcdfFile ncFile = NetcdfDatasets.openDataset(url)) {
final Variable var = ncFile.findVariable("enhancedVar");
assertThat((Object) var).isNotNull();
final Array values = var.read();
assertThat(nearlyEquals(values, Array.makeFromJavaArray(expectedValues))).isTrue();
}
}

@Test
public void shouldUseNcmlToModifyDatasetScanAsciiValues() {
final String path = "/dodsC/EnhancedDatasetScan/testgrid1.nc.ascii?enhancedVar%5B0:1:1%5D%5B0:1:1%5D";
final String endpoint = TestOnLocalServer.withHttpPath(path);

final byte[] content = TestOnLocalServer.getContent(endpoint, HttpServletResponse.SC_OK);
final String stringContent = new String(content, StandardCharsets.UTF_8);
final DecimalFormat df = new DecimalFormat("#.#");
Arrays.stream(expectedValues).forEach(d -> assertThat(stringContent).contains(df.format(d)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
import ucar.httpservices.HTTPFactory;
import ucar.httpservices.HTTPMethod;
import ucar.httpservices.HTTPSession;
import ucar.ma2.Array;
import ucar.nc2.NetcdfFile;
import ucar.nc2.NetcdfFiles;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.dataset.NetcdfDatasets;
import ucar.nc2.dt.grid.GeoGrid;
import ucar.nc2.dt.grid.GridDataset;
import ucar.nc2.ffi.netcdf.NetcdfClibrary;
import ucar.unidata.util.test.category.NeedsCdmUnitTest;
Expand All @@ -32,6 +34,7 @@

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
import static ucar.ma2.MAMath.nearlyEquals;

@Category(NeedsCdmUnitTest.class)
public class NcssGridIntegrationTest {
Expand Down Expand Up @@ -72,14 +75,43 @@ private void openBinaryOld(byte[] content, String gridName) throws IOException {
*/

@Test
public void checkGrid() throws Exception {
String endpoint = TestOnLocalServer.withHttpPath(
"/ncss/grid/gribCollection/GFS_CONUS_80km/GFS_CONUS_80km_20120227_0000.grib1?var=Temperature_isobaric");
public void checkGridForDifferentFormats() throws Exception {
checkGrid("");
checkGrid("&accept=netcdf");
checkGrid("&accept=netcdf3");
checkGrid("&accept=netcdf4");
checkGrid("&accept=netcdf4-classic");
checkGrid("&accept=netcdf4ext");
}

private void checkGrid(String acceptParameter) throws Exception {
final String path =
"/ncss/grid/gribCollection/GFS_CONUS_80km/GFS_CONUS_80km_20120227_0000.grib1?var=Temperature_isobaric";
final String endpoint = TestOnLocalServer.withHttpPath(path + acceptParameter);

byte[] content = TestOnLocalServer.getContent(endpoint, 200, ContentType.netcdf);
openBinaryNew(content, "Temperature_isobaric");
}

@Test
public void shouldWriteLongAttributeToNetcdf4ExtendedModel() throws Exception {
final String path = "/ncss/grid/scanLocal/testGridWithLongAttribute.nc4?var=var&accept=netcdf4ext";
final String endpoint = TestOnLocalServer.withHttpPath(path);

byte[] content = TestOnLocalServer.getContent(endpoint, 200, ContentType.netcdf);

try (NetcdfFile nf = NetcdfFiles.openInMemory("test_data.nc", content)) {
assertThat(nf.findGlobalAttribute("globalLongAttribute")).isNotNull();

GridDataset gdsDataset = new GridDataset(NetcdfDatasets.enhance(nf, NetcdfDataset.getDefaultEnhanceMode(), null));
GeoGrid grid = gdsDataset.findGridByName("var");
assertThat(grid).isNotNull();

Array data = grid.readDataSlice(-1, -1, -1, -1);
assertThat(nearlyEquals(data, Array.makeFromJavaArray(new int[] {0, 1, 2, 3}))).isTrue();
}
}

@Ignore("TODO Fix S3 FeatureCollection index path")
@Test
public void checkS3GridWithNonTopLevelS3Key() throws IOException {
Expand Down Expand Up @@ -142,7 +174,7 @@ public void shouldReturnCorrectFileTypeForAcceptParameter() throws HTTPException
checkFileType("netcdf", HttpServletResponse.SC_OK, ".nc");
checkFileType("netcdf4-classic", HttpServletResponse.SC_OK, ".nc4");
checkFileType("netcdf4", HttpServletResponse.SC_OK, ".nc4");
checkFileType("netcdf4ext", HttpServletResponse.SC_BAD_REQUEST, ".nc4"); // Not currently enabled in TdsInit
checkFileType("netcdf4ext", HttpServletResponse.SC_OK, ".nc4");
}

private void checkFileType(String acceptParameter, int expectedResponseCode, String expectedSuffix)
Expand Down
Loading

0 comments on commit e6ca3ee

Please sign in to comment.