Replaced all IllegalStateException with DataException in arbitrary code

This was necessary to ensure that all exceptions are caught intentionally, as otherwise it creates endless amounts of edge cases.
This commit is contained in:
CalDescent 2021-11-19 21:42:03 +00:00
parent 8cb06bf451
commit 73e609fa29
25 changed files with 209 additions and 185 deletions

View File

@ -36,7 +36,7 @@ public class ArbitraryDataBuildQueueItem {
public void build() throws IOException, DataException, MissingDataException { public void build() throws IOException, DataException, MissingDataException {
Long now = NTP.getTime(); Long now = NTP.getTime();
if (now == null) { if (now == null) {
throw new IllegalStateException("NTP time hasn't synced yet"); throw new DataException("NTP time hasn't synced yet");
} }
this.buildStartTimestamp = now; this.buildStartTimestamp = now;

View File

@ -88,7 +88,7 @@ public class ArbitraryDataBuilder {
if (latestPut == null) { if (latestPut == null) {
String message = String.format("Couldn't find PUT transaction for name %s, service %s and identifier %s", String message = String.format("Couldn't find PUT transaction for name %s, service %s and identifier %s",
this.name, this.service, this.identifierString()); this.name, this.service, this.identifierString());
throw new IllegalStateException(message); throw new DataException(message);
} }
this.latestPutTransaction = latestPut; this.latestPutTransaction = latestPut;
@ -101,25 +101,25 @@ public class ArbitraryDataBuilder {
} }
} }
private void validateTransactions() { private void validateTransactions() throws DataException {
List<ArbitraryTransactionData> transactionDataList = new ArrayList<>(this.transactions); List<ArbitraryTransactionData> transactionDataList = new ArrayList<>(this.transactions);
ArbitraryTransactionData latestPut = this.latestPutTransaction; ArbitraryTransactionData latestPut = this.latestPutTransaction;
if (latestPut == null) { if (latestPut == null) {
throw new IllegalStateException("Cannot PATCH without existing PUT. Deploy using PUT first."); throw new DataException("Cannot PATCH without existing PUT. Deploy using PUT first.");
} }
if (latestPut.getMethod() != Method.PUT) { if (latestPut.getMethod() != Method.PUT) {
throw new IllegalStateException("Expected PUT but received PATCH"); throw new DataException("Expected PUT but received PATCH");
} }
if (transactionDataList.size() == 0) { if (transactionDataList.size() == 0) {
throw new IllegalStateException(String.format("No transactions found for name %s, service %s, " + throw new DataException(String.format("No transactions found for name %s, service %s, " +
"identifier: %s, since %d", name, service, this.identifierString(), latestPut.getTimestamp())); "identifier: %s, since %d", name, service, this.identifierString(), latestPut.getTimestamp()));
} }
// Verify that the signature of the first transaction matches the latest PUT // Verify that the signature of the first transaction matches the latest PUT
ArbitraryTransactionData firstTransaction = transactionDataList.get(0); ArbitraryTransactionData firstTransaction = transactionDataList.get(0);
if (!Arrays.equals(firstTransaction.getSignature(), latestPut.getSignature())) { if (!Arrays.equals(firstTransaction.getSignature(), latestPut.getSignature())) {
throw new IllegalStateException("First transaction did not match latest PUT transaction"); throw new DataException("First transaction did not match latest PUT transaction");
} }
// Remove the first transaction, as it should be the only PUT // Remove the first transaction, as it should be the only PUT
@ -127,10 +127,10 @@ public class ArbitraryDataBuilder {
for (ArbitraryTransactionData transactionData : transactionDataList) { for (ArbitraryTransactionData transactionData : transactionDataList) {
if (transactionData == null) { if (transactionData == null) {
throw new IllegalStateException("Transaction not found"); throw new DataException("Transaction not found");
} }
if (transactionData.getMethod() != Method.PATCH) { if (transactionData.getMethod() != Method.PATCH) {
throw new IllegalStateException("Expected PATCH but received PUT"); throw new DataException("Expected PATCH but received PUT");
} }
} }
} }
@ -173,32 +173,32 @@ public class ArbitraryDataBuilder {
// By this point we should have all data needed to build the layers // By this point we should have all data needed to build the layers
Path path = arbitraryDataReader.getFilePath(); Path path = arbitraryDataReader.getFilePath();
if (path == null) { if (path == null) {
throw new IllegalStateException(String.format("Null path when building data from transaction %s", sig58)); throw new DataException(String.format("Null path when building data from transaction %s", sig58));
} }
if (!Files.exists(path)) { if (!Files.exists(path)) {
throw new IllegalStateException(String.format("Path doesn't exist when building data from transaction %s", sig58)); throw new DataException(String.format("Path doesn't exist when building data from transaction %s", sig58));
} }
paths.add(path); paths.add(path);
} }
} }
private void findLatestSignature() { private void findLatestSignature() throws DataException {
if (this.transactions.size() == 0) { if (this.transactions.size() == 0) {
throw new IllegalStateException("Unable to find latest signature from empty transaction list"); throw new DataException("Unable to find latest signature from empty transaction list");
} }
// Find the latest signature // Find the latest signature
ArbitraryTransactionData latestTransaction = this.transactions.get(this.transactions.size() - 1); ArbitraryTransactionData latestTransaction = this.transactions.get(this.transactions.size() - 1);
if (latestTransaction == null) { if (latestTransaction == null) {
throw new IllegalStateException("Unable to find latest signature from null transaction"); throw new DataException("Unable to find latest signature from null transaction");
} }
this.latestSignature = latestTransaction.getSignature(); this.latestSignature = latestTransaction.getSignature();
} }
private void validatePaths() { private void validatePaths() throws DataException {
if (this.paths.isEmpty()) { if (this.paths.isEmpty()) {
throw new IllegalStateException("No paths available from which to build latest state"); throw new DataException("No paths available from which to build latest state");
} }
} }
@ -238,7 +238,7 @@ public class ArbitraryDataBuilder {
private void cacheLatestSignature() throws IOException, DataException { private void cacheLatestSignature() throws IOException, DataException {
byte[] latestTransactionSignature = this.transactions.get(this.transactions.size()-1).getSignature(); byte[] latestTransactionSignature = this.transactions.get(this.transactions.size()-1).getSignature();
if (latestTransactionSignature == null) { if (latestTransactionSignature == null) {
throw new IllegalStateException("Missing latest transaction signature"); throw new DataException("Missing latest transaction signature");
} }
Long now = NTP.getTime(); Long now = NTP.getTime();
if (now == null) { if (now == null) {

View File

@ -153,7 +153,7 @@ public class ArbitraryDataCache {
cache.read(); cache.read();
return cache.getSignature(); return cache.getSignature();
} catch (IOException e) { } catch (IOException | DataException e) {
return null; return null;
} }
} }

View File

@ -79,12 +79,12 @@ public class ArbitraryDataCombiner {
} }
} }
private void preExecute() { private void preExecute() throws DataException {
if (this.pathBefore == null || this.pathAfter == null) { if (this.pathBefore == null || this.pathAfter == null) {
throw new IllegalStateException("No paths available to build patch"); throw new DataException("No paths available to build patch");
} }
if (!Files.exists(this.pathBefore) || !Files.exists(this.pathAfter)) { if (!Files.exists(this.pathBefore) || !Files.exists(this.pathAfter)) {
throw new IllegalStateException("Unable to create patch because at least one path doesn't exist"); throw new DataException("Unable to create patch because at least one path doesn't exist");
} }
} }
@ -92,35 +92,35 @@ public class ArbitraryDataCombiner {
} }
private void readMetadata() throws IOException { private void readMetadata() throws IOException, DataException {
this.metadata = new ArbitraryDataMetadataPatch(this.pathAfter); this.metadata = new ArbitraryDataMetadataPatch(this.pathAfter);
this.metadata.read(); this.metadata.read();
} }
private void validatePreviousSignature() { private void validatePreviousSignature() throws DataException {
if (this.signatureBefore == null) { if (this.signatureBefore == null) {
throw new IllegalStateException("No previous signature passed to the combiner"); throw new DataException("No previous signature passed to the combiner");
} }
byte[] previousSignature = this.metadata.getPreviousSignature(); byte[] previousSignature = this.metadata.getPreviousSignature();
if (previousSignature == null) { if (previousSignature == null) {
throw new IllegalStateException("Unable to extract previous signature from patch metadata"); throw new DataException("Unable to extract previous signature from patch metadata");
} }
// Compare the signatures // Compare the signatures
if (!Arrays.equals(previousSignature, this.signatureBefore)) { if (!Arrays.equals(previousSignature, this.signatureBefore)) {
throw new IllegalStateException("Previous signatures do not match - transactions out of order?"); throw new DataException("Previous signatures do not match - transactions out of order?");
} }
} }
private void validatePreviousHash() throws IOException { private void validatePreviousHash() throws IOException, DataException {
if (!Settings.getInstance().shouldValidateAllDataLayers()) { if (!Settings.getInstance().shouldValidateAllDataLayers()) {
return; return;
} }
byte[] previousHash = this.metadata.getPreviousHash(); byte[] previousHash = this.metadata.getPreviousHash();
if (previousHash == null) { if (previousHash == null) {
throw new IllegalStateException("Unable to extract previous hash from patch metadata"); throw new DataException("Unable to extract previous hash from patch metadata");
} }
ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathBefore); ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathBefore);
@ -139,14 +139,14 @@ public class ArbitraryDataCombiner {
this.finalPath = merge.getMergePath(); this.finalPath = merge.getMergePath();
} }
private void validateCurrentHash() throws IOException { private void validateCurrentHash() throws IOException, DataException {
if (!this.shouldValidateHashes) { if (!this.shouldValidateHashes) {
return; return;
} }
byte[] currentHash = this.metadata.getCurrentHash(); byte[] currentHash = this.metadata.getCurrentHash();
if (currentHash == null) { if (currentHash == null) {
throw new IllegalStateException("Unable to extract current hash from patch metadata"); throw new DataException("Unable to extract current hash from patch metadata");
} }
ArbitraryDataDigest digest = new ArbitraryDataDigest(this.finalPath); ArbitraryDataDigest digest = new ArbitraryDataDigest(this.finalPath);

View File

@ -48,12 +48,12 @@ public class ArbitraryDataCreatePatch {
} }
} }
private void preExecute() { private void preExecute() throws DataException {
if (this.pathBefore == null || this.pathAfter == null) { if (this.pathBefore == null || this.pathAfter == null) {
throw new IllegalStateException("No paths available to build patch"); throw new DataException("No paths available to build patch");
} }
if (!Files.exists(this.pathBefore) || !Files.exists(this.pathAfter)) { if (!Files.exists(this.pathBefore) || !Files.exists(this.pathAfter)) {
throw new IllegalStateException("Unable to create patch because at least one path doesn't exist"); throw new DataException("Unable to create patch because at least one path doesn't exist");
} }
this.createRandomIdentifier(); this.createRandomIdentifier();
@ -84,14 +84,14 @@ public class ArbitraryDataCreatePatch {
this.identifier = UUID.randomUUID().toString(); this.identifier = UUID.randomUUID().toString();
} }
private void createWorkingDirectory() { private void createWorkingDirectory() throws DataException {
// Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware
String baseDir = Settings.getInstance().getTempDataPath(); String baseDir = Settings.getInstance().getTempDataPath();
Path tempDir = Paths.get(baseDir, "patch", this.identifier); Path tempDir = Paths.get(baseDir, "patch", this.identifier);
try { try {
Files.createDirectories(tempDir); Files.createDirectories(tempDir);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to create temp directory"); throw new DataException("Unable to create temp directory");
} }
this.workingPath = tempDir; this.workingPath = tempDir;
} }
@ -114,7 +114,7 @@ public class ArbitraryDataCreatePatch {
} }
} }
private void process() throws IOException { private void process() throws IOException, DataException {
ArbitraryDataDiff diff = new ArbitraryDataDiff(this.pathBefore, this.pathAfter, this.previousSignature); ArbitraryDataDiff diff = new ArbitraryDataDiff(this.pathBefore, this.pathAfter, this.previousSignature);
this.finalPath = diff.getDiffPath(); this.finalPath = diff.getDiffPath();

View File

@ -6,6 +6,7 @@ import org.json.JSONObject;
import org.qortal.arbitrary.metadata.ArbitraryDataMetadataPatch; import org.qortal.arbitrary.metadata.ArbitraryDataMetadataPatch;
import org.qortal.arbitrary.patch.UnifiedDiffPatch; import org.qortal.arbitrary.patch.UnifiedDiffPatch;
import org.qortal.crypto.Crypto; import org.qortal.crypto.Crypto;
import org.qortal.repository.DataException;
import org.qortal.settings.Settings; import org.qortal.settings.Settings;
import java.io.*; import java.io.*;
@ -75,7 +76,7 @@ public class ArbitraryDataDiff {
private int totalFileCount; private int totalFileCount;
public ArbitraryDataDiff(Path pathBefore, Path pathAfter, byte[] previousSignature) { public ArbitraryDataDiff(Path pathBefore, Path pathAfter, byte[] previousSignature) throws DataException {
this.pathBefore = pathBefore; this.pathBefore = pathBefore;
this.pathAfter = pathAfter; this.pathAfter = pathAfter;
this.previousSignature = previousSignature; this.previousSignature = previousSignature;
@ -88,7 +89,7 @@ public class ArbitraryDataDiff {
this.createOutputDirectory(); this.createOutputDirectory();
} }
public void compute() throws IOException { public void compute() throws IOException, DataException {
try { try {
this.preExecute(); this.preExecute();
this.hashPreviousState(); this.hashPreviousState();
@ -115,19 +116,19 @@ public class ArbitraryDataDiff {
this.identifier = UUID.randomUUID().toString(); this.identifier = UUID.randomUUID().toString();
} }
private void createOutputDirectory() { private void createOutputDirectory() throws DataException {
// Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware
String baseDir = Settings.getInstance().getTempDataPath(); String baseDir = Settings.getInstance().getTempDataPath();
Path tempDir = Paths.get(baseDir, "diff", this.identifier); Path tempDir = Paths.get(baseDir, "diff", this.identifier);
try { try {
Files.createDirectories(tempDir); Files.createDirectories(tempDir);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to create temp directory"); throw new DataException("Unable to create temp directory");
} }
this.diffPath = tempDir; this.diffPath = tempDir;
} }
private void hashPreviousState() throws IOException { private void hashPreviousState() throws IOException, DataException {
ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathBefore); ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathBefore);
digest.compute(); digest.compute();
this.previousHash = digest.getHash(); this.previousHash = digest.getHash();
@ -181,7 +182,12 @@ public class ArbitraryDataDiff {
diff.copyFilePathToBaseDir(afterPathAbsolute, diffPathAbsolute, afterPathRelative); diff.copyFilePathToBaseDir(afterPathAbsolute, diffPathAbsolute, afterPathRelative);
} }
if (wasModified) { if (wasModified) {
try {
diff.pathModified(beforePathAbsolute, afterPathAbsolute, afterPathRelative, diffPathAbsolute); diff.pathModified(beforePathAbsolute, afterPathAbsolute, afterPathRelative, diffPathAbsolute);
} catch (DataException e) {
// We can only throw IOExceptions because we are overriding FileVisitor.visitFile()
throw new IOException(e);
}
} }
// Keep a tally of the total number of files to help with decision making // Keep a tally of the total number of files to help with decision making
@ -273,19 +279,19 @@ public class ArbitraryDataDiff {
} }
} }
private void validate() { private void validate() throws DataException {
if (this.addedPaths.isEmpty() && this.modifiedPaths.isEmpty() && this.removedPaths.isEmpty()) { if (this.addedPaths.isEmpty() && this.modifiedPaths.isEmpty() && this.removedPaths.isEmpty()) {
throw new IllegalStateException("Current state matches previous state. Nothing to do."); throw new DataException("Current state matches previous state. Nothing to do.");
} }
} }
private void hashCurrentState() throws IOException { private void hashCurrentState() throws IOException, DataException {
ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathAfter); ArbitraryDataDigest digest = new ArbitraryDataDigest(this.pathAfter);
digest.compute(); digest.compute();
this.currentHash = digest.getHash(); this.currentHash = digest.getHash();
} }
private void writeMetadata() throws IOException { private void writeMetadata() throws IOException, DataException {
ArbitraryDataMetadataPatch metadata = new ArbitraryDataMetadataPatch(this.diffPath); ArbitraryDataMetadataPatch metadata = new ArbitraryDataMetadataPatch(this.diffPath);
metadata.setAddedPaths(this.addedPaths); metadata.setAddedPaths(this.addedPaths);
metadata.setModifiedPaths(this.modifiedPaths); metadata.setModifiedPaths(this.modifiedPaths);
@ -298,7 +304,7 @@ public class ArbitraryDataDiff {
private void pathModified(Path beforePathAbsolute, Path afterPathAbsolute, Path afterPathRelative, private void pathModified(Path beforePathAbsolute, Path afterPathAbsolute, Path afterPathRelative,
Path destinationBasePathAbsolute) throws IOException { Path destinationBasePathAbsolute) throws IOException, DataException {
Path destination = Paths.get(destinationBasePathAbsolute.toString(), afterPathRelative.toString()); Path destination = Paths.get(destinationBasePathAbsolute.toString(), afterPathRelative.toString());
long beforeSize = Files.size(beforePathAbsolute); long beforeSize = Files.size(beforePathAbsolute);

View File

@ -1,5 +1,6 @@
package org.qortal.arbitrary; package org.qortal.arbitrary;
import org.qortal.repository.DataException;
import org.qortal.utils.Base58; import org.qortal.utils.Base58;
import java.io.IOException; import java.io.IOException;
@ -21,7 +22,7 @@ public class ArbitraryDataDigest {
this.path = path; this.path = path;
} }
public void compute() throws IOException { public void compute() throws IOException, DataException {
List<Path> allPaths = Files.walk(path).filter(Files::isRegularFile).sorted().collect(Collectors.toList()); List<Path> allPaths = Files.walk(path).filter(Files::isRegularFile).sorted().collect(Collectors.toList());
Path basePathAbsolute = this.path.toAbsolutePath(); Path basePathAbsolute = this.path.toAbsolutePath();
@ -29,7 +30,7 @@ public class ArbitraryDataDigest {
try { try {
sha256 = MessageDigest.getInstance("SHA-256"); sha256 = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-256 hashing algorithm unavailable"); throw new DataException("SHA-256 hashing algorithm unavailable");
} }
for (Path path : allPaths) { for (Path path : allPaths) {

View File

@ -3,6 +3,7 @@ package org.qortal.arbitrary;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.qortal.crypto.Crypto; import org.qortal.crypto.Crypto;
import org.qortal.repository.DataException;
import org.qortal.settings.Settings; import org.qortal.settings.Settings;
import org.qortal.transform.transaction.TransactionTransformer; import org.qortal.transform.transaction.TransactionTransformer;
import org.qortal.utils.Base58; import org.qortal.utils.Base58;
@ -64,14 +65,14 @@ public class ArbitraryDataFile {
public ArbitraryDataFile() { public ArbitraryDataFile() {
} }
public ArbitraryDataFile(String hash58) { public ArbitraryDataFile(String hash58) throws DataException {
this.createDataDirectory(); this.createDataDirectory();
this.filePath = ArbitraryDataFile.getOutputFilePath(hash58, false); this.filePath = ArbitraryDataFile.getOutputFilePath(hash58, false);
this.chunks = new ArrayList<>(); this.chunks = new ArrayList<>();
this.hash58 = hash58; this.hash58 = hash58;
} }
public ArbitraryDataFile(byte[] fileContent) { public ArbitraryDataFile(byte[] fileContent) throws DataException {
if (fileContent == null) { if (fileContent == null) {
LOGGER.error("fileContent is null"); LOGGER.error("fileContent is null");
return; return;
@ -89,18 +90,18 @@ public class ArbitraryDataFile {
if (!this.hash58.equals(this.digest58())) { if (!this.hash58.equals(this.digest58())) {
LOGGER.error("Hash {} does not match file digest {}", this.hash58, this.digest58()); LOGGER.error("Hash {} does not match file digest {}", this.hash58, this.digest58());
this.delete(); this.delete();
throw new IllegalStateException("Data file digest validation failed"); throw new DataException("Data file digest validation failed");
} }
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to write data to file"); throw new DataException("Unable to write data to file");
} }
} }
public static ArbitraryDataFile fromHash58(String hash58) { public static ArbitraryDataFile fromHash58(String hash58) throws DataException {
return new ArbitraryDataFile(hash58); return new ArbitraryDataFile(hash58);
} }
public static ArbitraryDataFile fromHash(byte[] hash) { public static ArbitraryDataFile fromHash(byte[] hash) throws DataException {
return ArbitraryDataFile.fromHash58(Base58.encode(hash)); return ArbitraryDataFile.fromHash58(Base58.encode(hash));
} }
@ -126,7 +127,7 @@ public class ArbitraryDataFile {
} }
return arbitraryDataFile; return arbitraryDataFile;
} catch (IOException e) { } catch (IOException | DataException e) {
LOGGER.error("Couldn't compute digest for ArbitraryDataFile"); LOGGER.error("Couldn't compute digest for ArbitraryDataFile");
} }
} }
@ -150,7 +151,7 @@ public class ArbitraryDataFile {
return true; return true;
} }
private Path copyToDataDirectory(Path sourcePath) { private Path copyToDataDirectory(Path sourcePath) throws DataException {
if (this.hash58 == null || this.filePath == null) { if (this.hash58 == null || this.filePath == null) {
return null; return null;
} }
@ -160,11 +161,11 @@ public class ArbitraryDataFile {
try { try {
return Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING); return Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException(String.format("Unable to copy file %s to data directory %s", sourcePath, destPath)); throw new DataException(String.format("Unable to copy file %s to data directory %s", sourcePath, destPath));
} }
} }
public static Path getOutputFilePath(String hash58, boolean createDirectories) { public static Path getOutputFilePath(String hash58, boolean createDirectories) throws DataException {
if (hash58 == null) { if (hash58 == null) {
return null; return null;
} }
@ -176,7 +177,7 @@ public class ArbitraryDataFile {
try { try {
Files.createDirectories(directory); Files.createDirectories(directory);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to create data subdirectory"); throw new DataException("Unable to create data subdirectory");
} }
} }
return Paths.get(directory.toString(), hash58); return Paths.get(directory.toString(), hash58);
@ -208,7 +209,7 @@ public class ArbitraryDataFile {
this.chunks.add(chunk); this.chunks.add(chunk);
} }
public void addChunkHashes(byte[] chunks) { public void addChunkHashes(byte[] chunks) throws DataException {
if (chunks == null || chunks.length == 0) { if (chunks == null || chunks.length == 0) {
return; return;
} }
@ -234,7 +235,7 @@ public class ArbitraryDataFile {
return hashes; return hashes;
} }
public int split(int chunkSize) { public int split(int chunkSize) throws DataException {
try { try {
File file = this.getFile(); File file = this.getFile();
@ -256,14 +257,14 @@ public class ArbitraryDataFile {
if (validationResult == ValidationResult.OK) { if (validationResult == ValidationResult.OK) {
this.chunks.add(chunk); this.chunks.add(chunk);
} else { } else {
throw new IllegalStateException(String.format("Chunk %s is invalid", chunk)); throw new DataException(String.format("Chunk %s is invalid", chunk));
} }
} }
} }
} }
} }
} catch (Exception e) { } catch (Exception e) {
throw new IllegalStateException("Unable to split file into chunks"); throw new DataException("Unable to split file into chunks");
} }
return this.chunks.size(); return this.chunks.size();
@ -308,7 +309,7 @@ public class ArbitraryDataFile {
return true; return true;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
return false; return false;
} catch (IOException e) { } catch (IOException | DataException e) {
return false; return false;
} }
} }
@ -416,7 +417,7 @@ public class ArbitraryDataFile {
return false; return false;
} }
public boolean allChunksExist(byte[] chunks) { public boolean allChunksExist(byte[] chunks) throws DataException {
if (chunks == null) { if (chunks == null) {
return true; return true;
} }
@ -432,7 +433,7 @@ public class ArbitraryDataFile {
return true; return true;
} }
public boolean anyChunksExist(byte[] chunks) { public boolean anyChunksExist(byte[] chunks) throws DataException {
if (chunks == null) { if (chunks == null) {
return false; return false;
} }
@ -473,7 +474,7 @@ public class ArbitraryDataFile {
return this.chunks; return this.chunks;
} }
public byte[] chunkHashes() { public byte[] chunkHashes() throws DataException {
if (this.chunks != null && this.chunks.size() > 0) { if (this.chunks != null && this.chunks.size() > 0) {
// Return null if we only have one chunk, with the same hash as the parent // Return null if we only have one chunk, with the same hash as the parent
if (Arrays.equals(this.digest(), this.chunks.get(0).digest())) { if (Arrays.equals(this.digest(), this.chunks.get(0).digest())) {
@ -486,7 +487,7 @@ public class ArbitraryDataFile {
byte[] chunkHash = chunk.digest(); byte[] chunkHash = chunk.digest();
if (chunkHash.length != 32) { if (chunkHash.length != 32) {
LOGGER.info("Invalid chunk hash length: {}", chunkHash.length); LOGGER.info("Invalid chunk hash length: {}", chunkHash.length);
throw new IllegalStateException("Invalid chunk hash length"); throw new DataException("Invalid chunk hash length");
} }
outputStream.write(chunk.digest()); outputStream.write(chunk.digest());
} }

View File

@ -2,6 +2,7 @@ package org.qortal.arbitrary;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.qortal.repository.DataException;
import org.qortal.utils.Base58; import org.qortal.utils.Base58;
import java.io.IOException; import java.io.IOException;
@ -12,19 +13,19 @@ public class ArbitraryDataFileChunk extends ArbitraryDataFile {
private static final Logger LOGGER = LogManager.getLogger(ArbitraryDataFileChunk.class); private static final Logger LOGGER = LogManager.getLogger(ArbitraryDataFileChunk.class);
public ArbitraryDataFileChunk(String hash58) { public ArbitraryDataFileChunk(String hash58) throws DataException {
super(hash58); super(hash58);
} }
public ArbitraryDataFileChunk(byte[] fileContent) { public ArbitraryDataFileChunk(byte[] fileContent) throws DataException {
super(fileContent); super(fileContent);
} }
public static ArbitraryDataFileChunk fromHash58(String hash58) { public static ArbitraryDataFileChunk fromHash58(String hash58) throws DataException {
return new ArbitraryDataFileChunk(hash58); return new ArbitraryDataFileChunk(hash58);
} }
public static ArbitraryDataFileChunk fromHash(byte[] hash) { public static ArbitraryDataFileChunk fromHash(byte[] hash) throws DataException {
return ArbitraryDataFileChunk.fromHash58(Base58.encode(hash)); return ArbitraryDataFileChunk.fromHash58(Base58.encode(hash));
} }

View File

@ -44,7 +44,7 @@ public class ArbitraryDataMerge {
} }
} }
private void preExecute() { private void preExecute() throws DataException {
this.createRandomIdentifier(); this.createRandomIdentifier();
this.createOutputDirectory(); this.createOutputDirectory();
} }
@ -57,14 +57,14 @@ public class ArbitraryDataMerge {
this.identifier = UUID.randomUUID().toString(); this.identifier = UUID.randomUUID().toString();
} }
private void createOutputDirectory() { private void createOutputDirectory() throws DataException {
// Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware
String baseDir = Settings.getInstance().getTempDataPath(); String baseDir = Settings.getInstance().getTempDataPath();
Path tempDir = Paths.get(baseDir, "merge", this.identifier); Path tempDir = Paths.get(baseDir, "merge", this.identifier);
try { try {
Files.createDirectories(tempDir); Files.createDirectories(tempDir);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to create temp directory"); throw new DataException("Unable to create temp directory");
} }
this.mergePath = tempDir; this.mergePath = tempDir;
} }
@ -73,7 +73,7 @@ public class ArbitraryDataMerge {
ArbitraryDataMerge.copyDirPathToBaseDir(this.pathBefore, this.mergePath, Paths.get("")); ArbitraryDataMerge.copyDirPathToBaseDir(this.pathBefore, this.mergePath, Paths.get(""));
} }
private void loadMetadata() throws IOException { private void loadMetadata() throws IOException, DataException {
this.metadata = new ArbitraryDataMetadataPatch(this.pathAfter); this.metadata = new ArbitraryDataMetadataPatch(this.pathAfter);
this.metadata.read(); this.metadata.read();
} }

View File

@ -131,12 +131,11 @@ public class ArbitraryDataReader {
* If no exception is thrown, you can then use getFilePath() to access the data immediately after returning * If no exception is thrown, you can then use getFilePath() to access the data immediately after returning
* *
* @param overwrite - set to true to force rebuild an existing cache * @param overwrite - set to true to force rebuild an existing cache
* @throws IllegalStateException
* @throws IOException * @throws IOException
* @throws DataException * @throws DataException
* @throws MissingDataException * @throws MissingDataException
*/ */
public void loadSynchronously(boolean overwrite) throws IllegalStateException, IOException, DataException, MissingDataException { public void loadSynchronously(boolean overwrite) throws DataException, IOException, MissingDataException {
try { try {
ArbitraryDataCache cache = new ArbitraryDataCache(this.uncompressedPath, overwrite, ArbitraryDataCache cache = new ArbitraryDataCache(this.uncompressedPath, overwrite,
this.resourceId, this.resourceIdType, this.service, this.identifier); this.resourceId, this.resourceIdType, this.service, this.identifier);
@ -158,7 +157,7 @@ public class ArbitraryDataReader {
} }
} }
private void preExecute() { private void preExecute() throws DataException {
ArbitraryDataBuildManager.getInstance().setBuildInProgress(true); ArbitraryDataBuildManager.getInstance().setBuildInProgress(true);
this.createWorkingDirectory(); this.createWorkingDirectory();
this.createUncompressedDirectory(); this.createUncompressedDirectory();
@ -168,15 +167,15 @@ public class ArbitraryDataReader {
ArbitraryDataBuildManager.getInstance().setBuildInProgress(false); ArbitraryDataBuildManager.getInstance().setBuildInProgress(false);
} }
private void createWorkingDirectory() { private void createWorkingDirectory() throws DataException {
try { try {
Files.createDirectories(this.workingPath); Files.createDirectories(this.workingPath);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to create temp directory"); throw new DataException("Unable to create temp directory");
} }
} }
private void createUncompressedDirectory() { private void createUncompressedDirectory() throws DataException {
try { try {
// Create parent directory // Create parent directory
Files.createDirectories(this.uncompressedPath.getParent()); Files.createDirectories(this.uncompressedPath.getParent());
@ -184,7 +183,7 @@ public class ArbitraryDataReader {
FileUtils.deleteDirectory(this.uncompressedPath.toFile()); FileUtils.deleteDirectory(this.uncompressedPath.toFile());
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to create uncompressed directory"); throw new DataException("Unable to create uncompressed directory");
} }
} }
@ -225,7 +224,7 @@ public class ArbitraryDataReader {
} }
} }
private void fetch() throws IllegalStateException, IOException, DataException, MissingDataException { private void fetch() throws DataException, IOException, MissingDataException {
switch (resourceIdType) { switch (resourceIdType) {
case FILE_HASH: case FILE_HASH:
@ -245,18 +244,18 @@ public class ArbitraryDataReader {
break; break;
default: default:
throw new IllegalStateException(String.format("Unknown resource ID type specified: %s", resourceIdType.toString())); throw new DataException(String.format("Unknown resource ID type specified: %s", resourceIdType.toString()));
} }
} }
private void fetchFromFileHash() { private void fetchFromFileHash() throws DataException {
// Load data file directly from the hash // Load data file directly from the hash
ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash58(resourceId); ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash58(resourceId);
// Set filePath to the location of the ArbitraryDataFile // Set filePath to the location of the ArbitraryDataFile
this.filePath = arbitraryDataFile.getFilePath(); this.filePath = arbitraryDataFile.getFilePath();
} }
private void fetchFromName() throws IllegalStateException, IOException, DataException, MissingDataException { private void fetchFromName() throws DataException, IOException, MissingDataException {
try { try {
// Build the existing state using past transactions // Build the existing state using past transactions
@ -264,7 +263,7 @@ public class ArbitraryDataReader {
builder.build(); builder.build();
Path builtPath = builder.getFinalPath(); Path builtPath = builder.getFinalPath();
if (builtPath == null) { if (builtPath == null) {
throw new IllegalStateException("Unable to build path"); throw new DataException("Unable to build path");
} }
// Update stats // Update stats
@ -282,7 +281,7 @@ public class ArbitraryDataReader {
} }
} }
private void fetchFromSignature() throws IllegalStateException, IOException, DataException, MissingDataException { private void fetchFromSignature() throws DataException, IOException, MissingDataException {
// Load the full transaction data from the database so we can access the file hashes // Load the full transaction data from the database so we can access the file hashes
ArbitraryTransactionData transactionData; ArbitraryTransactionData transactionData;
@ -290,15 +289,15 @@ public class ArbitraryDataReader {
transactionData = (ArbitraryTransactionData) repository.getTransactionRepository().fromSignature(Base58.decode(resourceId)); transactionData = (ArbitraryTransactionData) repository.getTransactionRepository().fromSignature(Base58.decode(resourceId));
} }
if (transactionData == null) { if (transactionData == null) {
throw new IllegalStateException(String.format("Transaction data not found for signature %s", this.resourceId)); throw new DataException(String.format("Transaction data not found for signature %s", this.resourceId));
} }
this.fetchFromTransactionData(transactionData); this.fetchFromTransactionData(transactionData);
} }
private void fetchFromTransactionData(ArbitraryTransactionData transactionData) throws IllegalStateException, IOException, MissingDataException { private void fetchFromTransactionData(ArbitraryTransactionData transactionData) throws DataException, IOException, MissingDataException {
if (transactionData == null) { if (transactionData == null) {
throw new IllegalStateException(String.format("Transaction data not found for signature %s", this.resourceId)); throw new DataException(String.format("Transaction data not found for signature %s", this.resourceId));
} }
// Load hashes // Load hashes
@ -316,7 +315,7 @@ public class ArbitraryDataReader {
if (!arbitraryDataFile.exists()) { if (!arbitraryDataFile.exists()) {
if (!arbitraryDataFile.allChunksExist(chunkHashes) || chunkHashes == null) { if (!arbitraryDataFile.allChunksExist(chunkHashes) || chunkHashes == null) {
if (ArbitraryDataStorageManager.getInstance().isNameInBlacklist(transactionData.getName())) { if (ArbitraryDataStorageManager.getInstance().isNameInBlacklist(transactionData.getName())) {
throw new IllegalStateException( throw new DataException(
String.format("Unable to request missing data for file %s due to blacklist", arbitraryDataFile)); String.format("Unable to request missing data for file %s due to blacklist", arbitraryDataFile));
} }
else { else {
@ -352,13 +351,13 @@ public class ArbitraryDataReader {
} }
// Ensure the complete hash matches the joined chunks // Ensure the complete hash matches the joined chunks
if (!Arrays.equals(arbitraryDataFile.digest(), digest)) { if (!Arrays.equals(arbitraryDataFile.digest(), digest)) {
throw new IllegalStateException("Unable to validate complete file hash"); throw new DataException("Unable to validate complete file hash");
} }
// Set filePath to the location of the ArbitraryDataFile // Set filePath to the location of the ArbitraryDataFile
this.filePath = arbitraryDataFile.getFilePath(); this.filePath = arbitraryDataFile.getFilePath();
} }
private void decrypt() { private void decrypt() throws DataException {
// Decrypt if we have the secret key. // Decrypt if we have the secret key.
byte[] secret = this.secret58 != null ? Base58.decode(this.secret58) : null; byte[] secret = this.secret58 != null ? Base58.decode(this.secret58) : null;
if (secret != null && secret.length == Transformer.AES256_LENGTH) { if (secret != null && secret.length == Transformer.AES256_LENGTH) {
@ -373,7 +372,7 @@ public class ArbitraryDataReader {
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException
| BadPaddingException | IllegalBlockSizeException | IOException | InvalidKeyException e) { | BadPaddingException | IllegalBlockSizeException | IOException | InvalidKeyException e) {
throw new IllegalStateException(String.format("Unable to decrypt file at path %s: %s", this.filePath, e.getMessage())); throw new DataException(String.format("Unable to decrypt file at path %s: %s", this.filePath, e.getMessage()));
} }
} else { } else {
// Assume it is unencrypted. This will be the case when we have built a custom path by combining // Assume it is unencrypted. This will be the case when we have built a custom path by combining
@ -381,9 +380,9 @@ public class ArbitraryDataReader {
} }
} }
private void uncompress() throws IOException { private void uncompress() throws IOException, DataException {
if (this.filePath == null || !Files.exists(this.filePath)) { if (this.filePath == null || !Files.exists(this.filePath)) {
throw new IllegalStateException("Can't uncompress non-existent file path"); throw new DataException("Can't uncompress non-existent file path");
} }
File file = new File(this.filePath.toString()); File file = new File(this.filePath.toString());
if (file.isDirectory()) { if (file.isDirectory()) {
@ -407,10 +406,10 @@ public class ArbitraryDataReader {
this.filePath.toFile().renameTo(finalPath.toFile()); this.filePath.toFile().renameTo(finalPath.toFile());
} }
else { else {
throw new IllegalStateException(String.format("Unrecognized compression type: %s", transactionData.getCompression())); throw new DataException(String.format("Unrecognized compression type: %s", transactionData.getCompression()));
} }
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException(String.format("Unable to unzip file: %s", e.getMessage())); throw new DataException(String.format("Unable to unzip file: %s", e.getMessage()));
} }
// If unzipped data was a file not a directory, move it into a data/ directory so that the .qortal // If unzipped data was a file not a directory, move it into a data/ directory so that the .qortal
@ -449,12 +448,12 @@ public class ArbitraryDataReader {
} }
private void moveFilePathToFinalDestination() throws IOException { private void moveFilePathToFinalDestination() throws IOException, DataException {
if (this.filePath.compareTo(this.uncompressedPath) != 0) { if (this.filePath.compareTo(this.uncompressedPath) != 0) {
File source = new File(this.filePath.toString()); File source = new File(this.filePath.toString());
File dest = new File(this.uncompressedPath.toString()); File dest = new File(this.uncompressedPath.toString());
if (!source.exists()) { if (!source.exists()) {
throw new IllegalStateException("Source directory doesn't exist"); throw new DataException("Source directory doesn't exist");
} }
// Ensure destination directory doesn't exist // Ensure destination directory doesn't exist
FileUtils.deleteDirectory(dest); FileUtils.deleteDirectory(dest);

View File

@ -65,7 +65,7 @@ public class ArbitraryDataResource {
} catch (MissingDataException e) { } catch (MissingDataException e) {
return new ArbitraryResourceSummary(ArbitraryResourceStatus.DOWNLOADING); return new ArbitraryResourceSummary(ArbitraryResourceStatus.DOWNLOADING);
} catch (IOException | DataException | IllegalStateException e) { } catch (IOException | DataException e) {
// Ignore for now // Ignore for now
} }

View File

@ -122,7 +122,7 @@ public class ArbitraryDataTransactionBuilder {
// State is appropriate for a PATCH transaction // State is appropriate for a PATCH transaction
return Method.PATCH; return Method.PATCH;
} }
catch (IOException | DataException | MissingDataException | IllegalStateException e) { catch (IOException | DataException | MissingDataException e) {
// Handle matching states separately, as it's best to block transactions with duplicate states // Handle matching states separately, as it's best to block transactions with duplicate states
if (e.getMessage().equals("Current state matches previous state. Nothing to do.")) { if (e.getMessage().equals("Current state matches previous state. Nothing to do.")) {
throw new DataException(e.getMessage()); throw new DataException(e.getMessage());

View File

@ -56,7 +56,7 @@ public class ArbitraryDataWriter {
this.compression = compression; this.compression = compression;
} }
public void save() throws IllegalStateException, IOException, DataException, InterruptedException, MissingDataException { public void save() throws DataException, IOException, DataException, InterruptedException, MissingDataException {
try { try {
this.preExecute(); this.preExecute();
this.validateService(); this.validateService();
@ -71,11 +71,11 @@ public class ArbitraryDataWriter {
} }
} }
private void preExecute() { private void preExecute() throws DataException {
// Enforce compression when uploading a directory // Enforce compression when uploading a directory
File file = new File(this.filePath.toString()); File file = new File(this.filePath.toString());
if (file.isDirectory() && compression == Compression.NONE) { if (file.isDirectory() && compression == Compression.NONE) {
throw new IllegalStateException("Unable to upload a directory without compression"); throw new DataException("Unable to upload a directory without compression");
} }
// Create temporary working directory // Create temporary working directory
@ -86,7 +86,7 @@ public class ArbitraryDataWriter {
this.cleanupFilesystem(); this.cleanupFilesystem();
} }
private void createWorkingDirectory() { private void createWorkingDirectory() throws DataException {
// Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware
String baseDir = Settings.getInstance().getTempDataPath(); String baseDir = Settings.getInstance().getTempDataPath();
String identifier = Base58.encode(Crypto.digest(this.filePath.toString().getBytes())); String identifier = Base58.encode(Crypto.digest(this.filePath.toString().getBytes()));
@ -94,7 +94,7 @@ public class ArbitraryDataWriter {
try { try {
Files.createDirectories(tempDir); Files.createDirectories(tempDir);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to create temp directory"); throw new DataException("Unable to create temp directory");
} }
this.workingPath = tempDir; this.workingPath = tempDir;
} }
@ -124,7 +124,7 @@ public class ArbitraryDataWriter {
break; break;
default: default:
throw new IllegalStateException(String.format("Unknown method specified: %s", method.toString())); throw new DataException(String.format("Unknown method specified: %s", method.toString()));
} }
} }
@ -154,29 +154,29 @@ public class ArbitraryDataWriter {
this.validatePatch(); this.validatePatch();
} }
private void validatePatch() { private void validatePatch() throws DataException {
if (this.filePath == null) { if (this.filePath == null) {
throw new IllegalStateException("Null path after creating patch"); throw new DataException("Null path after creating patch");
} }
File qortalMetadataDirectoryFile = Paths.get(this.filePath.toString(), ".qortal").toFile(); File qortalMetadataDirectoryFile = Paths.get(this.filePath.toString(), ".qortal").toFile();
if (!qortalMetadataDirectoryFile.exists()) { if (!qortalMetadataDirectoryFile.exists()) {
throw new IllegalStateException("Qortal metadata folder doesn't exist in patch"); throw new DataException("Qortal metadata folder doesn't exist in patch");
} }
if (!qortalMetadataDirectoryFile.isDirectory()) { if (!qortalMetadataDirectoryFile.isDirectory()) {
throw new IllegalStateException("Qortal metadata folder isn't a directory"); throw new DataException("Qortal metadata folder isn't a directory");
} }
File qortalPatchMetadataFile = Paths.get(this.filePath.toString(), ".qortal", "patch").toFile(); File qortalPatchMetadataFile = Paths.get(this.filePath.toString(), ".qortal", "patch").toFile();
if (!qortalPatchMetadataFile.exists()) { if (!qortalPatchMetadataFile.exists()) {
throw new IllegalStateException("Qortal patch metadata file doesn't exist in patch"); throw new DataException("Qortal patch metadata file doesn't exist in patch");
} }
if (!qortalPatchMetadataFile.isFile()) { if (!qortalPatchMetadataFile.isFile()) {
throw new IllegalStateException("Qortal patch metadata file isn't a file"); throw new DataException("Qortal patch metadata file isn't a file");
} }
} }
private void compress() throws InterruptedException { private void compress() throws InterruptedException, DataException {
// Compress the data if requested // Compress the data if requested
if (this.compression != Compression.NONE) { if (this.compression != Compression.NONE) {
this.compressedPath = Paths.get(this.workingPath.toString() + File.separator + "data.zip"); this.compressedPath = Paths.get(this.workingPath.toString() + File.separator + "data.zip");
@ -187,7 +187,7 @@ public class ArbitraryDataWriter {
ZipUtils.zip(this.filePath.toString(), this.compressedPath.toString(), "data"); ZipUtils.zip(this.filePath.toString(), this.compressedPath.toString(), "data");
} }
else { else {
throw new IllegalStateException(String.format("Unknown compression type specified: %s", compression.toString())); throw new DataException(String.format("Unknown compression type specified: %s", compression.toString()));
} }
// FUTURE: other compression types // FUTURE: other compression types
@ -199,13 +199,13 @@ public class ArbitraryDataWriter {
// Replace filePath pointer with the zipped file path // Replace filePath pointer with the zipped file path
this.filePath = this.compressedPath; this.filePath = this.compressedPath;
} catch (IOException e) { } catch (IOException | DataException e) {
throw new IllegalStateException("Unable to zip directory", e); throw new DataException("Unable to zip directory", e);
} }
} }
} }
private void encrypt() { private void encrypt() throws DataException {
this.encryptedPath = Paths.get(this.workingPath.toString() + File.separator + "data.zip.encrypted"); this.encryptedPath = Paths.get(this.workingPath.toString() + File.separator + "data.zip.encrypted");
try { try {
// Encrypt the file with AES // Encrypt the file with AES
@ -222,11 +222,11 @@ public class ArbitraryDataWriter {
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException
| BadPaddingException | IllegalBlockSizeException | IOException | InvalidKeyException e) { | BadPaddingException | IllegalBlockSizeException | IOException | InvalidKeyException e) {
throw new IllegalStateException(String.format("Unable to encrypt file %s: %s", this.filePath, e.getMessage())); throw new DataException(String.format("Unable to encrypt file %s: %s", this.filePath, e.getMessage()));
} }
} }
private void validate() throws IOException { private void validate() throws IOException, DataException {
if (this.arbitraryDataFile == null) { if (this.arbitraryDataFile == null) {
throw new IOException("No file available when validating"); throw new IOException("No file available when validating");
} }
@ -235,7 +235,7 @@ public class ArbitraryDataWriter {
// Validate the file // Validate the file
ValidationResult validationResult = this.arbitraryDataFile.isValid(); ValidationResult validationResult = this.arbitraryDataFile.isValid();
if (validationResult != ValidationResult.OK) { if (validationResult != ValidationResult.OK) {
throw new IllegalStateException(String.format("File %s failed validation: %s", this.arbitraryDataFile, validationResult)); throw new DataException(String.format("File %s failed validation: %s", this.arbitraryDataFile, validationResult));
} }
LOGGER.info("Whole file hash is valid: {}", this.arbitraryDataFile.digest58()); LOGGER.info("Whole file hash is valid: {}", this.arbitraryDataFile.digest58());
@ -243,14 +243,14 @@ public class ArbitraryDataWriter {
for (ArbitraryDataFileChunk chunk : this.arbitraryDataFile.getChunks()) { for (ArbitraryDataFileChunk chunk : this.arbitraryDataFile.getChunks()) {
validationResult = chunk.isValid(); validationResult = chunk.isValid();
if (validationResult != ValidationResult.OK) { if (validationResult != ValidationResult.OK) {
throw new IllegalStateException(String.format("Chunk %s failed validation: %s", chunk, validationResult)); throw new DataException(String.format("Chunk %s failed validation: %s", chunk, validationResult));
} }
} }
LOGGER.info("Chunk hashes are valid"); LOGGER.info("Chunk hashes are valid");
} }
private void split() throws IOException { private void split() throws IOException, DataException {
this.arbitraryDataFile = ArbitraryDataFile.fromPath(this.filePath); this.arbitraryDataFile = ArbitraryDataFile.fromPath(this.filePath);
if (this.arbitraryDataFile == null) { if (this.arbitraryDataFile == null) {
throw new IOException("No file available when trying to split"); throw new IOException("No file available when trying to split");
@ -261,7 +261,7 @@ public class ArbitraryDataWriter {
LOGGER.info(String.format("Successfully split into %d chunk%s", chunkCount, (chunkCount == 1 ? "" : "s"))); LOGGER.info(String.format("Successfully split into %d chunk%s", chunkCount, (chunkCount == 1 ? "" : "s")));
} }
else { else {
throw new IllegalStateException("Unable to split file into chunks"); throw new DataException("Unable to split file into chunks");
} }
} }

View File

@ -2,6 +2,7 @@ package org.qortal.arbitrary.metadata;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.qortal.repository.DataException;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
@ -30,7 +31,7 @@ public class ArbitraryDataMetadata {
return null; return null;
} }
protected void readJson() { protected void readJson() throws DataException {
// To be overridden // To be overridden
} }
@ -39,12 +40,12 @@ public class ArbitraryDataMetadata {
} }
public void read() throws IOException { public void read() throws IOException, DataException {
this.loadJson(); this.loadJson();
this.readJson(); this.readJson();
} }
public void write() throws IOException { public void write() throws IOException, DataException {
this.buildJson(); this.buildJson();
this.createQortalDirectory(); this.createQortalDirectory();
this.writeToQortalPath(); this.writeToQortalPath();
@ -61,11 +62,11 @@ public class ArbitraryDataMetadata {
this.jsonString = new String(Files.readAllBytes(path)); this.jsonString = new String(Files.readAllBytes(path));
} }
protected void createQortalDirectory() { protected void createQortalDirectory() throws DataException {
try { try {
Files.createDirectories(this.qortalDirectoryPath); Files.createDirectories(this.qortalDirectoryPath);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to create .qortal directory"); throw new DataException("Unable to create .qortal directory");
} }
} }

View File

@ -1,6 +1,7 @@
package org.qortal.arbitrary.metadata; package org.qortal.arbitrary.metadata;
import org.json.JSONObject; import org.json.JSONObject;
import org.qortal.repository.DataException;
import org.qortal.utils.Base58; import org.qortal.utils.Base58;
import java.nio.file.Path; import java.nio.file.Path;
@ -21,9 +22,9 @@ public class ArbitraryDataMetadataCache extends ArbitraryDataMetadata {
} }
@Override @Override
protected void readJson() { protected void readJson() throws DataException {
if (this.jsonString == null) { if (this.jsonString == null) {
throw new IllegalStateException("Patch JSON string is null"); throw new DataException("Patch JSON string is null");
} }
JSONObject cache = new JSONObject(this.jsonString); JSONObject cache = new JSONObject(this.jsonString);

View File

@ -5,6 +5,7 @@ import org.apache.logging.log4j.Logger;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.qortal.arbitrary.ArbitraryDataDiff.*; import org.qortal.arbitrary.ArbitraryDataDiff.*;
import org.qortal.repository.DataException;
import org.qortal.utils.Base58; import org.qortal.utils.Base58;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -39,9 +40,9 @@ public class ArbitraryDataMetadataPatch extends ArbitraryDataMetadata {
} }
@Override @Override
protected void readJson() { protected void readJson() throws DataException {
if (this.jsonString == null) { if (this.jsonString == null) {
throw new IllegalStateException("Patch JSON string is null"); throw new DataException("Patch JSON string is null");
} }
JSONObject patch = new JSONObject(this.jsonString); JSONObject patch = new JSONObject(this.jsonString);

View File

@ -8,6 +8,7 @@ import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.qortal.crypto.Crypto; import org.qortal.crypto.Crypto;
import org.qortal.repository.DataException;
import org.qortal.settings.Settings; import org.qortal.settings.Settings;
import java.io.BufferedWriter; import java.io.BufferedWriter;
@ -96,7 +97,7 @@ public class UnifiedDiffPatch {
* *
* @return true if valid, false if invalid * @return true if valid, false if invalid
*/ */
public boolean isValid() { public boolean isValid() throws DataException {
this.createRandomIdentifier(); this.createRandomIdentifier();
this.createTempValidationDirectory(); this.createTempValidationDirectory();
@ -147,7 +148,7 @@ public class UnifiedDiffPatch {
* @param pathSuffix - a file path to append to the base paths, or null if the base paths are already files * @param pathSuffix - a file path to append to the base paths, or null if the base paths are already files
* @throws IOException * @throws IOException
*/ */
public void apply(Path pathSuffix) throws IOException { public void apply(Path pathSuffix) throws IOException, DataException {
Path originalPath = this.before; Path originalPath = this.before;
Path patchPath = this.after; Path patchPath = this.after;
Path mergePath = this.destination; Path mergePath = this.destination;
@ -160,7 +161,7 @@ public class UnifiedDiffPatch {
} }
if (!patchPath.toFile().exists()) { if (!patchPath.toFile().exists()) {
throw new IllegalStateException("Patch file doesn't exist, but its path was included in modifiedPaths"); throw new DataException("Patch file doesn't exist, but its path was included in modifiedPaths");
} }
// Delete an existing file, as we are starting from a duplicate of pathBefore // Delete an existing file, as we are starting from a duplicate of pathBefore
@ -190,7 +191,7 @@ public class UnifiedDiffPatch {
writer.close(); writer.close();
} catch (PatchFailedException e) { } catch (PatchFailedException e) {
throw new IllegalStateException(String.format("Failed to apply patch for path %s: %s", pathSuffix, e.getMessage())); throw new DataException(String.format("Failed to apply patch for path %s: %s", pathSuffix, e.getMessage()));
} }
} }
@ -198,14 +199,14 @@ public class UnifiedDiffPatch {
this.identifier = UUID.randomUUID().toString(); this.identifier = UUID.randomUUID().toString();
} }
private void createTempValidationDirectory() { private void createTempValidationDirectory() throws DataException {
// Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware
String baseDir = Settings.getInstance().getTempDataPath(); String baseDir = Settings.getInstance().getTempDataPath();
Path tempDir = Paths.get(baseDir, "diff", "validate"); Path tempDir = Paths.get(baseDir, "diff", "validate");
try { try {
Files.createDirectories(tempDir); Files.createDirectories(tempDir);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("Unable to create temp directory"); throw new DataException("Unable to create temp directory");
} }
this.validationPath = tempDir; this.validationPath = tempDir;
} }

View File

@ -779,7 +779,9 @@ public class ArbitraryDataManager extends Thread {
byte[] hash = getArbitraryDataFileMessage.getHash(); byte[] hash = getArbitraryDataFileMessage.getHash();
Controller.getInstance().stats.getArbitraryDataFileMessageStats.requests.incrementAndGet(); Controller.getInstance().stats.getArbitraryDataFileMessageStats.requests.incrementAndGet();
try {
ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash(hash); ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash(hash);
if (arbitraryDataFile.exists()) { if (arbitraryDataFile.exists()) {
ArbitraryDataFileMessage arbitraryDataFileMessage = new ArbitraryDataFileMessage(arbitraryDataFile); ArbitraryDataFileMessage arbitraryDataFileMessage = new ArbitraryDataFileMessage(arbitraryDataFile);
arbitraryDataFileMessage.setId(message.getId()); arbitraryDataFileMessage.setId(message.getId());
@ -808,6 +810,10 @@ public class ArbitraryDataManager extends Thread {
LOGGER.info("Sent file-unknown response for file {}", arbitraryDataFile); LOGGER.info("Sent file-unknown response for file {}", arbitraryDataFile);
} }
} }
catch (DataException e) {
LOGGER.info("Unable to handle request for arbitrary data file: {}", Base58.encode(hash));
}
}
public void onNetworkGetArbitraryDataFileListMessage(Peer peer, Message message) { public void onNetworkGetArbitraryDataFileListMessage(Peer peer, Message message) {
GetArbitraryDataFileListMessage getArbitraryDataFileListMessage = (GetArbitraryDataFileListMessage) message; GetArbitraryDataFileListMessage getArbitraryDataFileListMessage = (GetArbitraryDataFileListMessage) message;

View File

@ -2,6 +2,7 @@ package org.qortal.network.message;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import org.qortal.arbitrary.ArbitraryDataFile; import org.qortal.arbitrary.ArbitraryDataFile;
import org.qortal.repository.DataException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@ -36,10 +37,15 @@ public class ArbitraryDataFileMessage extends Message {
byte[] data = new byte[dataLength]; byte[] data = new byte[dataLength];
byteBuffer.get(data); byteBuffer.get(data);
ArbitraryDataFile arbitraryDataFile = new ArbitraryDataFile(data);
try {
ArbitraryDataFile arbitraryDataFile = new ArbitraryDataFile(data);
return new ArbitraryDataFileMessage(id, arbitraryDataFile); return new ArbitraryDataFileMessage(id, arbitraryDataFile);
} }
catch (DataException e) {
return null;
}
}
@Override @Override
protected byte[] toData() { protected byte[] toData() {

View File

@ -77,7 +77,7 @@ public class ArbitraryTransactionUtils {
return hasNewerPut; return hasNewerPut;
} }
public static boolean completeFileExists(ArbitraryTransactionData transactionData) { public static boolean completeFileExists(ArbitraryTransactionData transactionData) throws DataException {
if (transactionData == null) { if (transactionData == null) {
return false; return false;
} }
@ -90,7 +90,7 @@ public class ArbitraryTransactionUtils {
} }
public static boolean allChunksExist(ArbitraryTransactionData transactionData) { public static boolean allChunksExist(ArbitraryTransactionData transactionData) throws DataException {
if (transactionData == null) { if (transactionData == null) {
return false; return false;
} }
@ -111,7 +111,7 @@ public class ArbitraryTransactionUtils {
return arbitraryDataFile.allChunksExist(chunkHashes); return arbitraryDataFile.allChunksExist(chunkHashes);
} }
public static boolean anyChunksExist(ArbitraryTransactionData transactionData) { public static boolean anyChunksExist(ArbitraryTransactionData transactionData) throws DataException {
if (transactionData == null) { if (transactionData == null) {
return false; return false;
} }
@ -132,7 +132,7 @@ public class ArbitraryTransactionUtils {
return arbitraryDataFile.anyChunksExist(chunkHashes); return arbitraryDataFile.anyChunksExist(chunkHashes);
} }
public static int ourChunkCount(ArbitraryTransactionData transactionData) { public static int ourChunkCount(ArbitraryTransactionData transactionData) throws DataException {
if (transactionData == null) { if (transactionData == null) {
return 0; return 0;
} }
@ -174,7 +174,7 @@ public class ArbitraryTransactionUtils {
return true; return true;
} }
public static boolean isFileHashRecent(byte[] hash, long now, long cleanupAfter) { public static boolean isFileHashRecent(byte[] hash, long now, long cleanupAfter) throws DataException {
ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash(hash); ArbitraryDataFile arbitraryDataFile = ArbitraryDataFile.fromHash(hash);
if (arbitraryDataFile == null || !arbitraryDataFile.exists()) { if (arbitraryDataFile == null || !arbitraryDataFile.exists()) {
// No hash, or file doesn't exist, so it's not recent // No hash, or file doesn't exist, so it's not recent
@ -185,7 +185,7 @@ public class ArbitraryTransactionUtils {
return ArbitraryTransactionUtils.isFileRecent(filePath, now, cleanupAfter); return ArbitraryTransactionUtils.isFileRecent(filePath, now, cleanupAfter);
} }
public static void deleteCompleteFile(ArbitraryTransactionData arbitraryTransactionData, long now, long cleanupAfter) { public static void deleteCompleteFile(ArbitraryTransactionData arbitraryTransactionData, long now, long cleanupAfter) throws DataException {
byte[] completeHash = arbitraryTransactionData.getData(); byte[] completeHash = arbitraryTransactionData.getData();
byte[] chunkHashes = arbitraryTransactionData.getChunkHashes(); byte[] chunkHashes = arbitraryTransactionData.getChunkHashes();
@ -200,7 +200,7 @@ public class ArbitraryTransactionUtils {
} }
} }
public static void deleteCompleteFileAndChunks(ArbitraryTransactionData arbitraryTransactionData) { public static void deleteCompleteFileAndChunks(ArbitraryTransactionData arbitraryTransactionData) throws DataException {
byte[] completeHash = arbitraryTransactionData.getData(); byte[] completeHash = arbitraryTransactionData.getData();
byte[] chunkHashes = arbitraryTransactionData.getChunkHashes(); byte[] chunkHashes = arbitraryTransactionData.getChunkHashes();
@ -209,7 +209,7 @@ public class ArbitraryTransactionUtils {
arbitraryDataFile.deleteAll(); arbitraryDataFile.deleteAll();
} }
public static void convertFileToChunks(ArbitraryTransactionData arbitraryTransactionData, long now, long cleanupAfter) { public static void convertFileToChunks(ArbitraryTransactionData arbitraryTransactionData, long now, long cleanupAfter) throws DataException {
byte[] completeHash = arbitraryTransactionData.getData(); byte[] completeHash = arbitraryTransactionData.getData();
byte[] chunkHashes = arbitraryTransactionData.getChunkHashes(); byte[] chunkHashes = arbitraryTransactionData.getChunkHashes();

View File

@ -22,7 +22,7 @@ public class ArbitraryDataDigestTests extends Common {
} }
@Test @Test
public void testDirectoryDigest() throws IOException { public void testDirectoryDigest() throws IOException, DataException {
Path dataPath = Paths.get("src/test/resources/arbitrary/demo1"); Path dataPath = Paths.get("src/test/resources/arbitrary/demo1");
String expectedHash58 = "DKyMuonWKoneJqiVHgw26Vk1ytrZG9PGsE9xfBg3GKDp"; String expectedHash58 = "DKyMuonWKoneJqiVHgw26Vk1ytrZG9PGsE9xfBg3GKDp";

View File

@ -18,7 +18,7 @@ public class ArbitraryDataFileTests extends Common {
} }
@Test @Test
public void testSplitAndJoin() { public void testSplitAndJoin() throws DataException {
String dummyDataString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; String dummyDataString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
ArbitraryDataFile arbitraryDataFile = new ArbitraryDataFile(dummyDataString.getBytes()); ArbitraryDataFile arbitraryDataFile = new ArbitraryDataFile(dummyDataString.getBytes());
assertTrue(arbitraryDataFile.exists()); assertTrue(arbitraryDataFile.exists());
@ -45,7 +45,7 @@ public class ArbitraryDataFileTests extends Common {
} }
@Test @Test
public void testSplitAndJoinWithLargeFiles() { public void testSplitAndJoinWithLargeFiles() throws DataException {
int fileSize = (int) (5.5f * 1024 * 1024); // 5.5MiB int fileSize = (int) (5.5f * 1024 * 1024); // 5.5MiB
byte[] randomData = new byte[fileSize]; byte[] randomData = new byte[fileSize];
new Random().nextBytes(randomData); // No need for SecureRandom here new Random().nextBytes(randomData); // No need for SecureRandom here

View File

@ -148,7 +148,7 @@ public class ArbitraryDataMergeTests extends Common {
patch.create(); patch.create();
fail("Creating patch should fail due to matching states"); fail("Creating patch should fail due to matching states");
} catch (IllegalStateException expectedException) { } catch (DataException expectedException) {
assertEquals("Current state matches previous state. Nothing to do.", expectedException.getMessage()); assertEquals("Current state matches previous state. Nothing to do.", expectedException.getMessage());
} }

View File

@ -234,7 +234,7 @@ public class ArbitraryDataTests extends Common {
arbitraryDataReader1a.loadSynchronously(true); arbitraryDataReader1a.loadSynchronously(true);
fail("Loading data with null identifier should fail due to nonexistent PUT transaction"); fail("Loading data with null identifier should fail due to nonexistent PUT transaction");
} catch (IllegalStateException expectedException) { } catch (DataException expectedException) {
assertEquals(String.format("Couldn't find PUT transaction for name %s, service %s " assertEquals(String.format("Couldn't find PUT transaction for name %s, service %s "
+ "and identifier ", name.toLowerCase(), service), expectedException.getMessage()); + "and identifier ", name.toLowerCase(), service), expectedException.getMessage());
} }
@ -246,7 +246,7 @@ public class ArbitraryDataTests extends Common {
arbitraryDataReader1b.loadSynchronously(true); arbitraryDataReader1b.loadSynchronously(true);
fail("Loading data with incorrect identifier should fail due to nonexistent PUT transaction"); fail("Loading data with incorrect identifier should fail due to nonexistent PUT transaction");
} catch (IllegalStateException expectedException) { } catch (DataException expectedException) {
assertEquals(String.format("Couldn't find PUT transaction for name %s, service %s " assertEquals(String.format("Couldn't find PUT transaction for name %s, service %s "
+ "and identifier %s", name.toLowerCase(), service, differentIdentifier), expectedException.getMessage()); + "and identifier %s", name.toLowerCase(), service, differentIdentifier), expectedException.getMessage());
} }
@ -321,7 +321,7 @@ public class ArbitraryDataTests extends Common {
arbitraryDataReader1c.loadSynchronously(true); arbitraryDataReader1c.loadSynchronously(true);
fail("Loading data with incorrect identifier should fail due to nonexistent PUT transaction"); fail("Loading data with incorrect identifier should fail due to nonexistent PUT transaction");
} catch (IllegalStateException expectedException) { } catch (DataException expectedException) {
assertEquals(String.format("Couldn't find PUT transaction for name %s, service %s " assertEquals(String.format("Couldn't find PUT transaction for name %s, service %s "
+ "and identifier %s", name.toLowerCase(), service, differentIdentifier), expectedException.getMessage()); + "and identifier %s", name.toLowerCase(), service, differentIdentifier), expectedException.getMessage());
} }