Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.DSSException;
import eu.europa.esig.dss.model.FileDocument;
import eu.europa.esig.dss.model.InMemoryDocument;
import eu.europa.esig.dss.spi.DSSUtils;
import eu.europa.esig.dss.spi.client.http.DSSCacheFileLoader;
import eu.europa.esig.dss.spi.client.http.DataLoader;
Expand All @@ -37,12 +38,8 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.function.Predicate;

/**
* This class provides some caching features to handle the resources. The default cache folder is set to
Expand Down Expand Up @@ -70,12 +67,20 @@ public class FileCacheDataLoader implements DataLoader, DSSCacheFileLoader {
/** List of URIs to be ignored */
private List<String> toIgnored;

/** List of conditions to be passed by data */
private List<Predicate<byte[]>> cacheConditions;

/** The cache expiration time, after which the document shall be downloaded again */
private long cacheExpirationTime = -1;

/** The dataloader to be used for a remote files access */
private DataLoader dataLoader;

/** The dataloader to be used as a fallback, when the data returned by {@link FileCacheDataLoader#dataLoader}
* does not pass the {@link FileCacheDataLoader#cacheConditions}
* */
private DataLoader fallbackDataLoader;

/**
* Empty constructor
*/
Expand Down Expand Up @@ -110,6 +115,23 @@ public void setDataLoader(DataLoader dataLoader) {
this.dataLoader = dataLoader;
}

/**
* Gets the fallback data loader
*
* @return {@link DataLoader}
*/
public DataLoader getFallbackDataLoader() {
return fallbackDataLoader;
}

/**
* Sets the fallback data loader
*
* @param fallbackDataLoader {@link DataLoader}
*/
public void setFallbackDataLoader(DataLoader fallbackDataLoader) {
this.fallbackDataLoader = fallbackDataLoader;
}
/**
* This method allows to set the file cache directory. If the cache folder does not exist then it's created.
*
Expand Down Expand Up @@ -193,6 +215,21 @@ public void addToBeIgnored(final String urlString) {
}
}

/**
* This method allows setting predicates, that the data returned by {@link FileCacheDataLoader#dataLoader} must
* pass in order to be cached and returned.
*
* @param cacheCondition
* the predicate to be tested with the returned data
*/
public void addCacheCondition(final Predicate<byte[]> cacheCondition) {
if (cacheConditions == null) {

cacheConditions = new ArrayList<>();
}
cacheConditions.add(cacheCondition);
}

/**
* Executes a GET request to the provided URL, with a forced cache {@code refresh} when defined
*
Expand Down Expand Up @@ -251,14 +288,32 @@ public DSSDocument getDocument(final String url, final boolean refresh) {
bytes = dataLoader.get(url);

}

if (Utils.isArrayNotEmpty(bytes)) {

if (Utils.isArrayEmpty(bytes)) {
throw new DSSExternalResourceException(String.format("Cannot retrieve data from url [%s]. Empty content is obtained!", url));
}
if (shouldBeCached(bytes)) {
final File out = createFile(fileName, bytes);
return new FileDocument(out);

}
throw new DSSExternalResourceException(String.format("Cannot retrieve data from url [%s]. Empty content is obtained!", url));

}
if (fileExists) {
LOG.warn("Data retrieved from url [{}] did not pass cache conditions! Returning earlier cached data", url);
return new FileDocument(file);
}
if (fallbackDataLoader != null) {
LOG.warn("Data retrieved from url [{}] did not pass cache conditions! Returning data from fallback data loader", url);
return new InMemoryDocument(fallbackDataLoader.get(url));
}
throw new DSSExternalResourceException(String.format("Data retrieved from url [%s] did not pass cache conditions!", url));

}

private boolean shouldBeCached(byte[] bytes) {
if (cacheConditions == null || cacheConditions.isEmpty()) {
return true;
}
return cacheConditions.stream()
.allMatch(cond -> cond.test(bytes));
}

@Override
Expand Down Expand Up @@ -383,12 +438,23 @@ public byte[] post(final String urlString, final byte[] content) throws DSSExcep
returnedBytes = dataLoader.post(urlString, content);
}

if (Utils.isArrayNotEmpty(returnedBytes)) {
if (Utils.isArrayEmpty(returnedBytes)) {
throw new DSSExternalResourceException(String.format("Cannot retrieve data from url [%s]. Empty content is obtained!", urlString));
}
if (shouldBeCached(returnedBytes)) {
final File cacheFile = getCacheFile(cacheFileName);
DSSUtils.saveToFile(returnedBytes, cacheFile);
return returnedBytes;
}
throw new DSSExternalResourceException(String.format("Cannot retrieve data from URL [%s]", urlString));
if (fileExists) {
LOG.warn("Data retrieved from url [{}] did not pass cache conditions! Returning earlier cached data", urlString);
return DSSUtils.toByteArray(file);
}
if (fallbackDataLoader != null) {
LOG.warn("Data retrieved from url [{}] did not pass cache conditions! Returning data from fallback data loader", urlString);
return fallbackDataLoader.get(urlString);
}
throw new DSSExternalResourceException(String.format("Data retrieved from url [%s] did not pass cache conditions!", urlString));
}

private boolean isCacheExpired(File file) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.DSSException;
import eu.europa.esig.dss.spi.DSSUtils;
import eu.europa.esig.dss.spi.client.http.DataLoader;
import eu.europa.esig.dss.spi.client.http.DataLoader.DataAndUrl;
import eu.europa.esig.dss.spi.client.http.IgnoreDataLoader;
import eu.europa.esig.dss.spi.client.http.MemoryDataLoader;
import eu.europa.esig.dss.spi.exception.DSSExternalResourceException;
import eu.europa.esig.dss.utils.Utils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -43,10 +45,7 @@
import java.util.concurrent.TimeUnit;

import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;

class FileCacheDataLoaderTest {

Expand Down Expand Up @@ -179,6 +178,25 @@ void offlineDataLoaderTest() throws IOException {
assertNotNull(dataAndUrl.getData());
}

@Test
void testCacheConditions() {
dataLoader.addCacheCondition(it -> false);
assertThrows(DSSExternalResourceException.class, () -> dataLoader.get(URL_TO_LOAD));
assertNull(getCachedFile(cacheDirectory));
}

@Test
void testFallbackDataLoader() {
Map<String, byte[]> dataMap = new HashMap<>();
dataMap.put(URL_TO_LOAD, URL_TO_LOAD.getBytes());
DataLoader fallbackDataLoader = new MemoryDataLoader(dataMap);
dataLoader.addCacheCondition(it -> false);
dataLoader.setFallbackDataLoader(fallbackDataLoader);

assertArrayEquals(URL_TO_LOAD.getBytes(), dataLoader.get(URL_TO_LOAD));
assertNull(getCachedFile(cacheDirectory));
}

private long getUrlAndReturnCacheCreationTime() {
byte[] bytesArray = dataLoader.get(URL_TO_LOAD);
assertTrue(bytesArray.length > 0);
Expand Down