diff --git a/README.md b/README.md index 723c854..3ed29a0 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ see [B2ApiClient.java](https://github.com/synapticloop/backblaze-b2-java-api/blo B2ApiClient() // authenticate the client -authorize(String, String) +authenticate(String, String) // create a bucket createBucket(String, BucketType) @@ -480,9 +480,9 @@ repositories { ``` dependencies { - runtime(group: 'synapticloop', name: 'backblaze-b2-java-api', version: '2.1.0', ext: 'jar') + runtime(group: 'synapticloop', name: 'backblaze-b2-java-api', version: '2.1.1', ext: 'jar') - compile(group: 'synapticloop', name: 'backblaze-b2-java-api', version: '2.1.0', ext: 'jar') + compile(group: 'synapticloop', name: 'backblaze-b2-java-api', version: '2.1.1', ext: 'jar') } ``` @@ -494,9 +494,9 @@ or, more simply for versions of gradle greater than 2.1 ``` dependencies { - runtime 'synapticloop:backblaze-b2-java-api:2.1.0' + runtime 'synapticloop:backblaze-b2-java-api:2.1.1' - compile 'synapticloop:backblaze-b2-java-api:2.1.0' + compile 'synapticloop:backblaze-b2-java-api:2.1.1' } ``` @@ -514,7 +514,7 @@ dependencies { synapticloop backblaze-b2-java-api - 2.1.0 + 2.1.1 jar ``` diff --git a/build.gradle b/build.gradle index 267652e..6c69bdf 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ group = 'synapticloop' archivesBaseName = 'backblaze-b2-java-api' description = """A java api for the truly excellent backblaze b2 storage service""" -version = '2.1.0' +version = '2.1.1' sourceCompatibility = 1.7 targetCompatibility = 1.7 diff --git a/src/main/java/synapticloop/b2/LifecycleRule.java b/src/main/java/synapticloop/b2/LifecycleRule.java new file mode 100644 index 0000000..c9d66cd --- /dev/null +++ b/src/main/java/synapticloop/b2/LifecycleRule.java @@ -0,0 +1,148 @@ +package synapticloop.b2; + +import org.json.JSONObject; + +/* + * Copyright (c) 2016 Synapticloop. + * + * All rights reserved. + * + * This code may contain contributions from other parties which, where + * applicable, will be listed in the default build file for the project + * ~and/or~ in a file named CONTRIBUTORS.txt in the root of the project. + * + * This source code and any derived binaries are covered by the terms and + * conditions of the Licence agreement ("the Licence"). You may not use this + * source code or any derived binaries except in compliance with the Licence. + * A copy of the Licence is available in the file named LICENSE.txt shipped with + * this source code or binaries. + */ + +/** + *

Lifecycle rules instruct the B2 service to automatically hide and/or delete + * old files. You can set up rules to do things like delete old versions of + * files 30 days after a newer version was uploaded.

+ * + *

A bucket can have up to 100 lifecycle rules. Each rule has a fileNamePrefix + * that specifies which files in the bucket it applies to. Any file whose name + * starts with the prefix is subject to the rule. A prefix of the empty string, + * "", means that the rule applies to all files in the bucket.

+ * + *

You're not allowed to create two rules that both apply to the same files. + * For example, a rule with a file name prefix of photos/ and a rule with a file + * name prefix of photos/kittens/ would both apply to a file named + * photos/kittens/fluffy.jpg, so you're not allowed to have both rules at the + * same time.

+ * + *

Each lifecycle rule specifies two things:

+ * + * + * + *

Either can be null, which means that part of the rule doesn't apply. Setting + * both to null is not allowed, because the rule would do nothing.

+ * + *

Setting daysFromUploadingToHiding to 0 is not allowed. When set, it must be a + * positive number.

+ * + *

The most commonly used setting is daysFromHidingToDeleting, which says how long + * to keep file versions that are not the current version. A file version counts + * as hidden when explicitly hidden with b2_hide_file, or when a newer file with + * the same name is uploaded. When a rule with this setting applies, the file will + * be deleted the given number of days after it is hidden.

+ * + *

For example, if you are backing up your files to B2 using the B2 command-line + * tool, or another software package that uploads files when they change, B2 will + * keep old versions of the file. This is very helpful, because it means the old + * versions are there if the original file is accidentally deleted. On the other + * hand, keeping them forever can clutter things up. For an application like this, + * you might want a lifecycle rule that keeps old versions in the backup/ folder + * for 30 days, and then deletes them:

+ * + *
+    {
+        "daysFromHidingToDeleting": 30,
+        "daysFromUploadingToHiding": null,
+        "fileNamePrefix": "backup/"
+    }
+ * 
+ * + *

The daysFromUploadingToHiding setting is less frequently used. It causes files to + * be hidden automatically after the given number of days. This can be useful for things + * like server log files that can be deleted after a while. This rule will keep files + * in the logs/ folder for 7 days, and then hide and immediately delete them:

+ * + *
+    {
+        "daysFromHidingToDeleting": 0,
+        "daysFromUploadingToHiding": 7,
+        "fileNamePrefix": "logs/"
+    }
+ * 
+ * + *

The API calls related to lifecycle rules are:

+ * + * + * + *

When you create a new bucket, you can specify the lifecycle rules. Later, + * you can change the rules on a bucket by updating it.

+ * + * @author synapticloop + * + */ +public class LifecycleRule { + private final Long daysFromHidingToDeleting; + private final Long daysFromUploadingToHiding; + private final String fileNamePrefix; + + public LifecycleRule(Long daysFromHidingToDeleting, Long daysFromUploadingToHiding, String fileNamePrefix) { + this.daysFromHidingToDeleting = daysFromHidingToDeleting; + this.daysFromUploadingToHiding = daysFromUploadingToHiding; + this.fileNamePrefix = fileNamePrefix; + } + + public LifecycleRule(JSONObject jsonObject) { + long hide = jsonObject.optLong("daysFromHidingToDeleting", Long.MIN_VALUE); + if(hide == Long.MIN_VALUE) { + daysFromHidingToDeleting = null; + } else { + daysFromHidingToDeleting = hide; + } + + long upload = jsonObject.optLong("daysFromUploadingToHiding", Long.MIN_VALUE); + if(upload == Long.MIN_VALUE) { + daysFromUploadingToHiding = null; + } else { + daysFromUploadingToHiding = upload; + } + + fileNamePrefix = jsonObject.optString("fileNamePrefix", null); + } + + /** + * The number of days from when the file was hidden to when it will be deleted + * + * @return the number of days from hiding to deleting + */ + public Long getDaysFromHidingToDeleting() { return this.daysFromHidingToDeleting; } + + /** + * The number of days from when the file was uploaded to when it will be deleted + * + * @return the number of days from uploading to deleting + */ + public Long getDaysFromUploadingToHiding() { return this.daysFromUploadingToHiding; } + + /** + * The file name prefix to which this rule applies + * + * @return the file name prefix to which this rule applies + */ + public String getFileNamePrefix() { return this.fileNamePrefix; } + +} diff --git a/src/main/java/synapticloop/b2/request/B2ListFileNamesRequest.java b/src/main/java/synapticloop/b2/request/B2ListFileNamesRequest.java index 97a934a..b8415b2 100644 --- a/src/main/java/synapticloop/b2/request/B2ListFileNamesRequest.java +++ b/src/main/java/synapticloop/b2/request/B2ListFileNamesRequest.java @@ -1,5 +1,7 @@ package synapticloop.b2.request; +import java.io.IOException; + /* * Copyright (c) 2016 Synapticloop. * @@ -19,22 +21,19 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; -import java.io.IOException; - import synapticloop.b2.exception.B2ApiException; import synapticloop.b2.response.B2AuthorizeAccountResponse; import synapticloop.b2.response.B2ListFilesResponse; /** *

Lists the names of all files in a bucket, starting at a given name.

- *

+ * *

This call returns at most 1000 file names, but it can be called repeatedly to scan through all of the file names in a bucket. Each time you call, it returns an "endFileName" that can be used as the starting point for the next call.

*

There may be many file versions for the same name, but this call will return each name only once. If you want all of the versions, use b2_list_file_versions instead.

- *

- *

- * This is the interaction class for the b2_list_file_names api calls, this was - * generated from the backblaze api documentation - which can be found here: - *

+ * + *

This is the interaction class for the b2_list_file_names api calls, this was + * generated from the backblaze api documentation - which can be found here:

+ * * http://www.backblaze.com/b2/docs/b2_list_file_names.html * * @author synapticloop diff --git a/src/main/java/synapticloop/b2/response/B2BucketResponse.java b/src/main/java/synapticloop/b2/response/B2BucketResponse.java index 739daca..d20c2f4 100644 --- a/src/main/java/synapticloop/b2/response/B2BucketResponse.java +++ b/src/main/java/synapticloop/b2/response/B2BucketResponse.java @@ -1,5 +1,11 @@ package synapticloop.b2.response; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.json.JSONArray; + /* * Copyright (c) 2016 Synapticloop. * @@ -21,6 +27,7 @@ import org.slf4j.LoggerFactory; import synapticloop.b2.BucketType; +import synapticloop.b2.LifecycleRule; import synapticloop.b2.exception.B2ApiException; public class B2BucketResponse extends BaseB2Response { @@ -30,6 +37,9 @@ public class B2BucketResponse extends BaseB2Response { private final String accountId; private final String bucketName; private final String bucketType; + private final Long revision; + private final Map bucketInfo; + private final List lifecycleRules = new ArrayList(); /** * Instantiate a bucket response with the JSON response as a string from @@ -46,6 +56,13 @@ public B2BucketResponse(String json) throws B2ApiException { this.accountId = this.readString(B2ResponseProperties.KEY_ACCOUNT_ID); this.bucketName = this.readString(B2ResponseProperties.KEY_BUCKET_NAME); this.bucketType = this.readString(B2ResponseProperties.KEY_BUCKET_TYPE); + this.revision = this.readLong(B2ResponseProperties.KEY_REVISION); + this.bucketInfo = this.readMap(B2ResponseProperties.KEY_BUCKET_INFO); + + JSONArray lifecycleObjects = this.readObjects(B2ResponseProperties.KEY_LIFECYCLE_RULES); + for (Object object : lifecycleObjects) { + lifecycleRules.add(new LifecycleRule((JSONObject)object)); + } this.warnOnMissedKeys(); } @@ -65,6 +82,13 @@ public B2BucketResponse(final JSONObject response) throws B2ApiException { this.accountId = this.readString(B2ResponseProperties.KEY_ACCOUNT_ID); this.bucketName = this.readString(B2ResponseProperties.KEY_BUCKET_NAME); this.bucketType = this.readString(B2ResponseProperties.KEY_BUCKET_TYPE); + this.revision = this.readLong(B2ResponseProperties.KEY_REVISION); + this.bucketInfo = this.readMap(B2ResponseProperties.KEY_BUCKET_INFO); + + JSONArray lifecycleObjects = this.readObjects(B2ResponseProperties.KEY_LIFECYCLE_RULES); + for (Object object : lifecycleObjects) { + lifecycleRules.add(new LifecycleRule((JSONObject)object)); + } this.warnOnMissedKeys(); } @@ -104,6 +128,28 @@ public BucketType getBucketType() { } } + /** + * Get the map of the bucket info for the bucket that was operated on, or an + * empty map if not set. + * + * @return the map of the file info for the file that was operated on + */ + public Map getBucketInfo() { return this.bucketInfo; } + + /** + * Get the revision number for the bucket + * + * @return the revision number for the bucket + */ + public long getRevision() { return this.revision; } + + /** + * Return the list of all of the lifecycle rules that apply to this bucket + * + * @return the list of all lifecycle rules + */ + public List getLifecycleRules() { return lifecycleRules; } + @Override protected Logger getLogger() { return LOGGER; } @@ -117,4 +163,5 @@ public String toString() { sb.append('}'); return sb.toString(); } + } diff --git a/src/main/java/synapticloop/b2/response/B2ResponseProperties.java b/src/main/java/synapticloop/b2/response/B2ResponseProperties.java index 2b6b99a..7c1daf1 100644 --- a/src/main/java/synapticloop/b2/response/B2ResponseProperties.java +++ b/src/main/java/synapticloop/b2/response/B2ResponseProperties.java @@ -22,8 +22,10 @@ public final class B2ResponseProperties { public static final String KEY_API_URL = "apiUrl"; public static final String KEY_AUTHORIZATION_TOKEN = "authorizationToken"; public static final String KEY_BUCKET_ID = "bucketId"; + public static final String KEY_BUCKET_INFO = "bucketInfo"; public static final String KEY_BUCKET_NAME = "bucketName"; public static final String KEY_BUCKET_TYPE = "bucketType"; + public static final String KEY_BUCKETS = "buckets"; public static final String KEY_CONTENT_LENGTH = "contentLength"; public static final String KEY_CONTENT_SHA1 = "contentSha1"; public static final String KEY_CONTENT_TYPE = "contentType"; @@ -32,14 +34,15 @@ public final class B2ResponseProperties { public static final String KEY_FILE_INFO = "fileInfo"; public static final String KEY_FILE_NAME = "fileName"; public static final String KEY_FILES = "files"; + public static final String KEY_LIFECYCLE_RULES = "lifecycleRules"; + public static final String KEY_MINIMUM_PART_SIZE = "minimumPartSize"; public static final String KEY_NEXT_FILE_ID = "nextFileId"; public static final String KEY_NEXT_FILE_NAME = "nextFileName"; + public static final String KEY_NEXT_PART_NUMBER = "nextPartNumber"; + public static final String KEY_PART_NUMBER = "partNumber"; + public static final String KEY_PARTS = "parts"; + public static final String KEY_REVISION = "revision"; public static final String KEY_SIZE = "size"; public static final String KEY_UPLOAD_TIMESTAMP = "uploadTimestamp"; public static final String KEY_UPLOAD_URL = "uploadUrl"; - public static final String KEY_BUCKETS = "buckets"; - public static final String KEY_PARTS = "parts"; - public static final String KEY_PART_NUMBER = "partNumber"; - public static final String KEY_NEXT_PART_NUMBER = "nextPartNumber"; - public static final String KEY_MINIMUM_PART_SIZE = "minimumPartSize"; } diff --git a/src/test/java/synapticloop/b2/request/B2DownloadFileRequestTest.java b/src/test/java/synapticloop/b2/request/B2DownloadFileRequestTest.java index 7fabad2..8ab5bd0 100644 --- a/src/test/java/synapticloop/b2/request/B2DownloadFileRequestTest.java +++ b/src/test/java/synapticloop/b2/request/B2DownloadFileRequestTest.java @@ -85,14 +85,14 @@ public void testDownloadFileByRange() throws Exception { 0, 5).getResponse(); - assertEquals(B2TestHelper.DUMMY_FILE_CONTENT.substring(0, 6), IOUtils.toString(b2DownloadFileResponse.getContent())); + assertEquals(B2TestHelper.DUMMY_FILE_CONTENT.substring(0, 6), IOUtils.toString(b2DownloadFileResponse.getContent(), Charset.defaultCharset())); b2DownloadFileResponse = new B2DownloadFileByIdRequest(HttpClients.createDefault(), B2TestHelper.getB2AuthorizeAccountResponse(), b2FileResponse.getFileId(), 0, 5).getResponse(); - assertEquals(B2TestHelper.DUMMY_FILE_CONTENT.substring(0, 6), IOUtils.toString(b2DownloadFileResponse.getContent())); + assertEquals(B2TestHelper.DUMMY_FILE_CONTENT.substring(0, 6), IOUtils.toString(b2DownloadFileResponse.getContent(), Charset.defaultCharset())); B2TestHelper.deleteFile(b2FileResponse.getFileName(), b2FileResponse.getFileId()); B2TestHelper.deleteBucket(randomPrivateBucket.getBucketId());