checked
30 | ) {
31 | this.checked = checked;
32 | }
33 |
34 | @Override
35 | @SuppressWarnings("PMD.AvoidCatchingGenericException")
36 | public T get() {
37 | try {
38 | return this.checked.get();
39 | } catch (final IOException err) {
40 | throw new ArtipieIOException(err);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/ArtipieException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie;
6 |
7 | /**
8 | * Base Artipie exception.
9 | * It should be used as a base exception for all Artipie public APIs
10 | * as a contract instead of others.
11 | *
12 | * @since 1.0
13 | * @implNote ArtipieException is unchecked exception, but it's a good
14 | * practice to document it via {@code throws} tag in JavaDocs.
15 | */
16 | public class ArtipieException extends RuntimeException {
17 |
18 | private static final long serialVersionUID = 1L;
19 |
20 | /**
21 | * New exception with message and base cause.
22 | * @param msg Message
23 | * @param cause Cause
24 | */
25 | public ArtipieException(final String msg, final Throwable cause) {
26 | super(msg, cause);
27 | }
28 |
29 | /**
30 | * New exception with base cause.
31 | * @param cause Cause
32 | */
33 | public ArtipieException(final Throwable cause) {
34 | super(cause);
35 | }
36 |
37 | /**
38 | * New exception with message.
39 | * @param msg Message
40 | */
41 | public ArtipieException(final String msg) {
42 | super(msg);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/key/KeyExcludeByIndex.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 |
6 | package com.artipie.asto.key;
7 |
8 | import com.artipie.asto.Key;
9 | import java.util.LinkedList;
10 | import java.util.List;
11 |
12 | /**
13 | * Key that excludes a part by its index.
14 | * @implNote If index is out of bounds, the class can return the origin key.
15 | * @since 1.9.1
16 | */
17 | public final class KeyExcludeByIndex extends Key.Wrap {
18 |
19 | /**
20 | * Ctor.
21 | * @param key Key
22 | * @param index Index of part
23 | */
24 | public KeyExcludeByIndex(final Key key, final int index) {
25 | super(
26 | new From(KeyExcludeByIndex.exclude(key, index))
27 | );
28 | }
29 |
30 | /**
31 | * Excludes part by its index.
32 | * @param key Key
33 | * @param index Index of part to exclude
34 | * @return List of parts
35 | */
36 | private static List exclude(final Key key, final int index) {
37 | final List parts = new LinkedList<>(key.parts());
38 | if (index >= 0 && index < parts.size()) {
39 | parts.remove(index);
40 | }
41 | return parts;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/misc/UncheckedIOScalar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.ArtipieException;
8 | import com.artipie.asto.ArtipieIOException;
9 | import java.io.IOException;
10 |
11 | /**
12 | * Scalar that throws {@link ArtipieException} on error.
13 | * @param Return value type
14 | * @since 1.3
15 | * @checkstyle AbbreviationAsWordInNameCheck (200 lines)
16 | */
17 | public final class UncheckedIOScalar implements Scalar {
18 |
19 | /**
20 | * Original origin.
21 | */
22 | private final UncheckedScalar.Checked origin;
23 |
24 | /**
25 | * Ctor.
26 | * @param origin Encapsulated origin
27 | */
28 | public UncheckedIOScalar(final UncheckedScalar.Checked origin) {
29 | this.origin = origin;
30 | }
31 |
32 | @Override
33 | @SuppressWarnings("PMD.AvoidCatchingGenericException")
34 | public T value() {
35 | try {
36 | return this.origin.value();
37 | // @checkstyle IllegalCatchCheck (1 line)
38 | } catch (final IOException ex) {
39 | throw new ArtipieIOException(ex);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/asto-etcd/src/main/java/com/artipie/asto/etcd/EtcdStorageFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.etcd;
6 |
7 | import com.artipie.asto.Storage;
8 | import com.artipie.asto.factory.ArtipieStorageFactory;
9 | import com.artipie.asto.factory.Config;
10 | import com.artipie.asto.factory.StorageFactory;
11 | import io.etcd.jetcd.Client;
12 | import io.etcd.jetcd.ClientBuilder;
13 | import java.time.Duration;
14 | import java.util.Arrays;
15 |
16 | /**
17 | * Etcd storage factory.
18 | * @since 0.1
19 | */
20 | @ArtipieStorageFactory("etcd")
21 | public final class EtcdStorageFactory implements StorageFactory {
22 | @Override
23 | public Storage newStorage(final Config cfg) {
24 | final Config connection = new Config.StrictStorageConfig(cfg)
25 | .config("connection");
26 | final String[] endpoints = connection.sequence("endpoints").toArray(new String[0]);
27 | final ClientBuilder builder = Client.builder().endpoints(endpoints);
28 | final String sto = connection.string("timeout");
29 | if (sto != null) {
30 | builder.connectTimeout(Duration.ofMillis(Integer.parseInt(sto)));
31 | }
32 | return new EtcdStorage(builder.build(), Arrays.toString(endpoints));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/FileStorageWhiteboxVerificationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import com.artipie.asto.fs.FileStorage;
8 | import com.artipie.asto.test.StorageWhiteboxVerification;
9 | import java.nio.file.Path;
10 | import java.util.Optional;
11 | import org.junit.jupiter.api.io.TempDir;
12 |
13 | /**
14 | * File storage verification test.
15 | *
16 | * @checkstyle ProtectedMethodInFinalClassCheck (500 lines)
17 | * @since 1.14.0
18 | */
19 | @SuppressWarnings("PMD.TestClassWithoutTestCases")
20 | public final class FileStorageWhiteboxVerificationTest extends StorageWhiteboxVerification {
21 |
22 | /**
23 | * Temp test dir.
24 | */
25 | @TempDir
26 | private Path temp;
27 |
28 | @Override
29 | protected Storage newStorage() {
30 | return new FileStorage(this.temp.resolve("base"));
31 | }
32 |
33 | @Override
34 | protected Optional newBaseForRootSubStorage() {
35 | return Optional.of(new FileStorage(this.temp.resolve("root-sub-storage")));
36 | }
37 |
38 | @Override
39 | protected Optional newBaseForSubStorage() throws Exception {
40 | return Optional.of(new FileStorage(this.temp.resolve("sub-storage")));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/OneTimePublisherVerificationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import io.reactivex.Flowable;
8 | import org.reactivestreams.Publisher;
9 | import org.reactivestreams.tck.PublisherVerification;
10 | import org.reactivestreams.tck.TestEnvironment;
11 |
12 | /**
13 | * Reactive streams-tck verification suit for {@link OneTimePublisher}.
14 | * @since 0.23
15 | */
16 | @SuppressWarnings("PMD.TestClassWithoutTestCases")
17 | public final class OneTimePublisherVerificationTest extends PublisherVerification {
18 |
19 | /**
20 | * Ctor.
21 | */
22 | public OneTimePublisherVerificationTest() {
23 | super(new TestEnvironment());
24 | }
25 |
26 | @Override
27 | public Publisher createPublisher(final long elements) {
28 | return Flowable.empty();
29 | }
30 |
31 | @Override
32 | public Publisher createFailedPublisher() {
33 | final OneTimePublisher publisher = new OneTimePublisher<>(Flowable.fromArray(1));
34 | Flowable.fromPublisher(publisher).toList().blockingGet();
35 | return publisher;
36 | }
37 |
38 | @Override
39 | public long maxElementsFromPublisher() {
40 | return 0;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/streams/PublishingOutputStreamTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.streams;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.ext.ContentAs;
9 | import io.reactivex.Single;
10 | import java.nio.charset.StandardCharsets;
11 | import org.hamcrest.MatcherAssert;
12 | import org.hamcrest.core.IsEqual;
13 | import org.junit.jupiter.api.Test;
14 |
15 | /**
16 | * Tests for {@link StorageValuePipeline.PublishingOutputStream}.
17 | *
18 | * @since 1.12
19 | */
20 | public final class PublishingOutputStreamTest {
21 | @Test
22 | void shouldPublishContentWhenDataIsWroteToOutputStream() throws Exception {
23 | final Content content;
24 | try (StorageValuePipeline.PublishingOutputStream output =
25 | new StorageValuePipeline.PublishingOutputStream()) {
26 | content = new Content.From(output.publisher());
27 | output.write("test data".getBytes(StandardCharsets.UTF_8));
28 | output.write(" test data 2".getBytes(StandardCharsets.UTF_8));
29 | }
30 | MatcherAssert.assertThat(
31 | ContentAs.STRING.apply(Single.just(content)).toFuture().get(),
32 | new IsEqual<>("test data test data 2")
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/key/KeyExcludeLastTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.key;
6 |
7 | import com.artipie.asto.Key;
8 | import org.hamcrest.MatcherAssert;
9 | import org.hamcrest.core.IsEqual;
10 | import org.junit.jupiter.api.Test;
11 |
12 | /**
13 | * Test case for {@link KeyExcludeLast}.
14 | *
15 | * @since 1.9.1
16 | */
17 | @SuppressWarnings("PMD.AvoidDuplicateLiterals")
18 | final class KeyExcludeLastTest {
19 |
20 | @Test
21 | void excludesLastPart() {
22 | final Key key = new Key.From("1", "2", "1");
23 | MatcherAssert.assertThat(
24 | new KeyExcludeLast(key, "1").string(),
25 | new IsEqual<>("1/2")
26 | );
27 | }
28 |
29 | @Test
30 | void excludesWhenPartIsNotAtEnd() {
31 | final Key key = new Key.From("one", "two", "three");
32 | MatcherAssert.assertThat(
33 | new KeyExcludeLast(key, "two").string(),
34 | new IsEqual<>("one/three")
35 | );
36 | }
37 |
38 | @Test
39 | void excludesNonExistingPart() {
40 | final Key key = new Key.From("3", "4");
41 | MatcherAssert.assertThat(
42 | new KeyExcludeLast(key, "5").string(),
43 | new IsEqual<>("3/4")
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/key/KeyExcludeFirstTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.key;
6 |
7 | import com.artipie.asto.Key;
8 | import org.hamcrest.MatcherAssert;
9 | import org.hamcrest.core.IsEqual;
10 | import org.junit.jupiter.api.Test;
11 |
12 | /**
13 | * Test case for {@link KeyExcludeFirst}.
14 | *
15 | * @since 1.8.1
16 | */
17 | @SuppressWarnings("PMD.AvoidDuplicateLiterals")
18 | final class KeyExcludeFirstTest {
19 |
20 | @Test
21 | void excludesFirstPart() {
22 | final Key key = new Key.From("1", "2", "1");
23 | MatcherAssert.assertThat(
24 | new KeyExcludeFirst(key, "1").string(),
25 | new IsEqual<>("2/1")
26 | );
27 | }
28 |
29 | @Test
30 | void excludesWhenPartIsNotAtBeginning() {
31 | final Key key = new Key.From("one", "two", "three");
32 | MatcherAssert.assertThat(
33 | new KeyExcludeFirst(key, "two").string(),
34 | new IsEqual<>("one/three")
35 | );
36 | }
37 |
38 | @Test
39 | void excludesNonExistingPart() {
40 | final Key key = new Key.From("1", "2");
41 | MatcherAssert.assertThat(
42 | new KeyExcludeFirst(key, "3").string(),
43 | new IsEqual<>("1/2")
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/asto-s3/src/test/java/com/artipie/asto/s3/S3HeadMetaTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.s3;
6 |
7 | import com.artipie.asto.Meta;
8 | import org.hamcrest.MatcherAssert;
9 | import org.hamcrest.core.IsEqual;
10 | import org.junit.jupiter.api.Test;
11 | import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
12 |
13 | /**
14 | * Test case for {@link S3HeadMeta}.
15 | * @since 0.1
16 | */
17 | final class S3HeadMetaTest {
18 |
19 | @Test
20 | void readSize() {
21 | final long len = 1024;
22 | MatcherAssert.assertThat(
23 | new S3HeadMeta(
24 | HeadObjectResponse.builder()
25 | .contentLength(len)
26 | .eTag("empty")
27 | .build()
28 | ).read(Meta.OP_SIZE).orElseThrow(IllegalStateException::new),
29 | new IsEqual<>(len)
30 | );
31 | }
32 |
33 | @Test
34 | void readHash() {
35 | final String hash = "abc";
36 | MatcherAssert.assertThat(
37 | new S3HeadMeta(
38 | HeadObjectResponse.builder()
39 | .contentLength(0L)
40 | .eTag(hash)
41 | .build()
42 | ).read(Meta.OP_MD5).orElseThrow(IllegalStateException::new),
43 | new IsEqual<>(hash)
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/memory/InMemoryStorageTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.memory;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.Key;
9 | import java.util.concurrent.TimeUnit;
10 | import org.hamcrest.MatcherAssert;
11 | import org.hamcrest.core.IsEqual;
12 | import org.junit.jupiter.api.BeforeEach;
13 | import org.junit.jupiter.api.Test;
14 | import org.junit.jupiter.api.Timeout;
15 |
16 | /**
17 | * Tests for {@link InMemoryStorage}.
18 | *
19 | * @since 0.18
20 | */
21 | class InMemoryStorageTest {
22 |
23 | /**
24 | * Storage being tested.
25 | */
26 | private InMemoryStorage storage;
27 |
28 | @BeforeEach
29 | void setUp() {
30 | this.storage = new InMemoryStorage();
31 | }
32 |
33 | @Test
34 | @Timeout(1)
35 | void shouldNotBeBlockedByEndlessContent() throws Exception {
36 | final Key.From key = new Key.From("data");
37 | this.storage.save(
38 | key,
39 | new Content.From(
40 | ignored -> {
41 | }
42 | )
43 | );
44 | // @checkstyle MagicNumberCheck (1 line)
45 | Thread.sleep(100);
46 | MatcherAssert.assertThat(
47 | this.storage.exists(key).get(1, TimeUnit.SECONDS),
48 | new IsEqual<>(false)
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/key/KeyExcludeFirst.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 |
6 | package com.artipie.asto.key;
7 |
8 | import com.artipie.asto.Key;
9 | import java.util.LinkedList;
10 | import java.util.List;
11 |
12 | /**
13 | * Key that excludes the first occurrence of a part.
14 | * @implNote If part to exclude was not found, the class can return the origin key.
15 | * @since 1.8.1
16 | */
17 | public final class KeyExcludeFirst extends Key.Wrap {
18 |
19 | /**
20 | * Ctor.
21 | * @param key Key
22 | * @param part Part to exclude
23 | */
24 | public KeyExcludeFirst(final Key key, final String part) {
25 | super(
26 | new Key.From(KeyExcludeFirst.exclude(key, part))
27 | );
28 | }
29 |
30 | /**
31 | * Excludes first occurrence of part.
32 | * @param key Key
33 | * @param part Part to exclude
34 | * @return List of parts
35 | */
36 | private static List exclude(final Key key, final String part) {
37 | final List parts = new LinkedList<>();
38 | boolean isfound = false;
39 | for (final String prt : key.parts()) {
40 | if (prt.equals(part) && !isfound) {
41 | isfound = true;
42 | continue;
43 | }
44 | parts.add(prt);
45 | }
46 | return parts;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/ext/ContentAsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.ext;
6 |
7 | import com.artipie.asto.Content;
8 | import io.reactivex.Single;
9 | import org.hamcrest.MatcherAssert;
10 | import org.hamcrest.core.IsEqual;
11 | import org.junit.jupiter.api.Test;
12 |
13 | /**
14 | * Test for {@link ContentAs}.
15 | * @since 0.33
16 | */
17 | class ContentAsTest {
18 |
19 | @Test
20 | void transformsToString() throws Exception {
21 | final String str = "abc012";
22 | MatcherAssert.assertThat(
23 | ContentAs.STRING.apply(Single.just(new Content.From(str.getBytes()))).toFuture().get(),
24 | new IsEqual<>(str)
25 | );
26 | }
27 |
28 | @Test
29 | void transformsToBytes() throws Exception {
30 | final byte[] bytes = "876hgf".getBytes();
31 | MatcherAssert.assertThat(
32 | ContentAs.BYTES.apply(Single.just(new Content.From(bytes))).toFuture().get(),
33 | new IsEqual<>(bytes)
34 | );
35 | }
36 |
37 | @Test
38 | void transformsToLong() throws Exception {
39 | final long number = 12_087L;
40 | MatcherAssert.assertThat(
41 | ContentAs.LONG.apply(
42 | Single.just(new Content.From(String.valueOf(number).getBytes()))
43 | ).toFuture().get(),
44 | new IsEqual<>(number)
45 | );
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/cache/CacheControlTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.cache;
6 |
7 | import com.artipie.asto.Key;
8 | import org.hamcrest.MatcherAssert;
9 | import org.hamcrest.Matchers;
10 | import org.junit.jupiter.params.ParameterizedTest;
11 | import org.junit.jupiter.params.provider.MethodSource;
12 |
13 | /**
14 | * Test case for {@link CacheControl}.
15 | *
16 | * @since 0.25
17 | */
18 | final class CacheControlTest {
19 |
20 | static Object[][] verifyAllItemsParams() {
21 | return new Object[][]{
22 | new Object[]{CacheControl.Standard.ALWAYS, CacheControl.Standard.ALWAYS, true},
23 | new Object[]{CacheControl.Standard.ALWAYS, CacheControl.Standard.NO_CACHE, false},
24 | new Object[]{CacheControl.Standard.NO_CACHE, CacheControl.Standard.ALWAYS, false},
25 | new Object[]{CacheControl.Standard.NO_CACHE, CacheControl.Standard.NO_CACHE, false},
26 | };
27 | }
28 |
29 | @ParameterizedTest
30 | @MethodSource("verifyAllItemsParams")
31 | void verifyAllItems(final CacheControl first, final CacheControl second,
32 | final boolean expects) throws Exception {
33 | MatcherAssert.assertThat(
34 | new CacheControl.All(first, second)
35 | .validate(Key.ROOT, Remote.EMPTY)
36 | .toCompletableFuture().get(),
37 | Matchers.is(expects)
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/ext/PublisherAsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.ext;
6 |
7 | import com.artipie.asto.Content;
8 | import java.nio.charset.StandardCharsets;
9 | import org.hamcrest.MatcherAssert;
10 | import org.hamcrest.core.IsEqual;
11 | import org.junit.jupiter.api.Test;
12 |
13 | /**
14 | * Test for {@link PublisherAs}.
15 | * @since 0.24
16 | */
17 | class PublisherAsTest {
18 |
19 | @Test
20 | void readsBytes() {
21 | final byte[] buf = "abc".getBytes();
22 | MatcherAssert.assertThat(
23 | new PublisherAs(new Content.From(buf)).bytes().toCompletableFuture().join(),
24 | new IsEqual<>(buf)
25 | );
26 | }
27 |
28 | @Test
29 | void readsAsciiString() {
30 | final byte[] buf = "абв".getBytes(StandardCharsets.US_ASCII);
31 | MatcherAssert.assertThat(
32 | new PublisherAs(new Content.From(buf)).asciiString().toCompletableFuture().join(),
33 | new IsEqual<>(new String(buf, StandardCharsets.US_ASCII))
34 | );
35 | }
36 |
37 | @Test
38 | void readsString() {
39 | final byte[] buf = "фыв".getBytes(StandardCharsets.UTF_8);
40 | MatcherAssert.assertThat(
41 | new PublisherAs(new Content.From(buf)).string(StandardCharsets.UTF_8)
42 | .toCompletableFuture().join(),
43 | new IsEqual<>(new String(buf, StandardCharsets.UTF_8))
44 | );
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/Remaining.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import java.nio.ByteBuffer;
8 |
9 | /**
10 | * Remaining bytes in a byte buffer.
11 | * @since 0.13
12 | */
13 | public final class Remaining {
14 |
15 | /**
16 | * The buffer.
17 | */
18 | private final ByteBuffer buf;
19 |
20 | /**
21 | * Restore buffer position.
22 | */
23 | private final boolean restore;
24 |
25 | /**
26 | * Ctor.
27 | * @param buf The byte buffer.
28 | */
29 | public Remaining(final ByteBuffer buf) {
30 | this(buf, false);
31 | }
32 |
33 | /**
34 | * Ctor.
35 | * @param buf The byte buffer.
36 | * @param restore Restore position.
37 | */
38 | public Remaining(final ByteBuffer buf, final boolean restore) {
39 | this.buf = buf;
40 | this.restore = restore;
41 | }
42 |
43 | /**
44 | * Obtain remaining bytes.
45 | *
46 | * Read all remaining bytes from the buffer and reset position back after
47 | * reading.
48 | *
49 | * @return Remaining bytes.
50 | */
51 | public byte[] bytes() {
52 | final byte[] bytes = new byte[this.buf.remaining()];
53 | if (this.restore) {
54 | this.buf.mark();
55 | }
56 | this.buf.get(bytes);
57 | if (this.restore) {
58 | this.buf.reset();
59 | }
60 | return bytes;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/misc/UncheckedScalar.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.ArtipieException;
8 |
9 | /**
10 | * Scalar that throws {@link com.artipie.ArtipieException} on error.
11 | * @param Return value type
12 | * @param Error type
13 | * @since 1.3
14 | */
15 | public final class UncheckedScalar implements Scalar {
16 |
17 | /**
18 | * Original origin.
19 | */
20 | private final Checked origin;
21 |
22 | /**
23 | * Ctor.
24 | * @param origin Encapsulated origin
25 | */
26 | public UncheckedScalar(final Checked origin) {
27 | this.origin = origin;
28 | }
29 |
30 | @Override
31 | @SuppressWarnings("PMD.AvoidCatchingGenericException")
32 | public T value() {
33 | try {
34 | return this.origin.value();
35 | // @checkstyle IllegalCatchCheck (1 line)
36 | } catch (final Exception ex) {
37 | throw new ArtipieException(ex);
38 | }
39 | }
40 |
41 | /**
42 | * Checked version of scalar.
43 | * @param Return type
44 | * @param Error type
45 | * @since 1.1
46 | */
47 | @FunctionalInterface
48 | public interface Checked {
49 |
50 | /**
51 | * Return value.
52 | * @return Result
53 | * @throws E On error
54 | */
55 | R value() throws E;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/asto-redis/src/main/java/com/artipie/asto/redis/RedisStorageFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.redis;
6 |
7 | import com.artipie.asto.ArtipieIOException;
8 | import com.artipie.asto.Storage;
9 | import com.artipie.asto.factory.ArtipieStorageFactory;
10 | import com.artipie.asto.factory.Config;
11 | import com.artipie.asto.factory.StorageFactory;
12 | import java.io.IOException;
13 | import org.redisson.Redisson;
14 | import org.redisson.api.RedissonClient;
15 |
16 | /**
17 | * Redis storage factory.
18 | *
19 | * @since 0.1
20 | */
21 | @ArtipieStorageFactory("redis")
22 | public final class RedisStorageFactory implements StorageFactory {
23 | /**
24 | * Default redis object name.
25 | */
26 | public static final String DEF_OBJ_NAME = "artipie-redis";
27 |
28 | @Override
29 | public Storage newStorage(final Config cfg) {
30 | try {
31 | String name = cfg.string("name");
32 | if (name == null) {
33 | name = RedisStorageFactory.DEF_OBJ_NAME;
34 | }
35 | final RedissonClient redisson = Redisson.create(
36 | org.redisson.config.Config.fromYAML(
37 | new Config.StrictStorageConfig(cfg)
38 | .string("config")
39 | )
40 | );
41 | return new RedisStorage(redisson.getMap(name), redisson.getId());
42 | } catch (final IOException err) {
43 | throw new ArtipieIOException(err);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/misc/UncheckedConsumer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.ArtipieException;
8 | import java.util.function.Consumer;
9 |
10 | /**
11 | * Unchecked {@link Consumer}.
12 | * @param Consumer type
13 | * @param Error type
14 | * @since 1.1
15 | */
16 | public final class UncheckedConsumer implements Consumer {
17 |
18 | /**
19 | * Checked version.
20 | */
21 | private final Checked checked;
22 |
23 | /**
24 | * Ctor.
25 | * @param checked Checked func
26 | */
27 | public UncheckedConsumer(final Checked checked) {
28 | this.checked = checked;
29 | }
30 |
31 | @Override
32 | @SuppressWarnings("PMD.AvoidCatchingGenericException")
33 | public void accept(final T val) {
34 | try {
35 | this.checked.accept(val);
36 | // @checkstyle IllegalCatchCheck (1 line)
37 | } catch (final Exception err) {
38 | throw new ArtipieException(err);
39 | }
40 | }
41 |
42 | /**
43 | * Checked version of consumer.
44 | * @param Consumer type
45 | * @param Error type
46 | * @since 1.1
47 | */
48 | @FunctionalInterface
49 | public interface Checked {
50 |
51 | /**
52 | * Accept value.
53 | * @param value Value to accept
54 | * @throws E On error
55 | */
56 | void accept(T value) throws E;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/ext/DigestsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.ext;
6 |
7 | import org.hamcrest.MatcherAssert;
8 | import org.hamcrest.core.IsEqual;
9 | import org.junit.jupiter.api.Assertions;
10 | import org.junit.jupiter.api.Test;
11 | import org.junit.jupiter.params.ParameterizedTest;
12 | import org.junit.jupiter.params.provider.CsvSource;
13 |
14 | /**
15 | * Test for {@link Digests}.
16 | * @since 0.24
17 | */
18 | class DigestsTest {
19 |
20 | @ParameterizedTest
21 | @CsvSource({
22 | "MD5,MD5",
23 | "SHA1,SHA-1",
24 | "SHA256,SHA-256",
25 | "SHA512,SHA-512"
26 | })
27 | void providesCorrectMessageDigestAlgorithm(final Digests item, final String expected) {
28 | MatcherAssert.assertThat(
29 | item.get().getAlgorithm(),
30 | new IsEqual<>(expected)
31 | );
32 | }
33 |
34 | @ParameterizedTest
35 | @CsvSource({
36 | "md5,MD5",
37 | "SHA-1,SHA1",
38 | "sha-256,SHA256",
39 | "SHa-512,SHA512"
40 | })
41 | void returnsCorrectDigestItem(final String from, final Digests item) {
42 | MatcherAssert.assertThat(
43 | new Digests.FromString(from).get(),
44 | new IsEqual<>(item)
45 | );
46 | }
47 |
48 | @Test
49 | void throwsExceptionOnUnknownAlgorithm() {
50 | Assertions.assertThrows(
51 | IllegalArgumentException.class,
52 | () -> new Digests.FromString("123").get()
53 | );
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/asto-etcd/src/test/java/com/artipie/asto/etcd/EtcdStorageFactoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.etcd;
6 |
7 | import com.amihaiemil.eoyaml.Yaml;
8 | import com.artipie.asto.factory.Config;
9 | import com.artipie.asto.factory.StoragesLoader;
10 | import org.hamcrest.MatcherAssert;
11 | import org.hamcrest.core.IsInstanceOf;
12 | import org.junit.jupiter.api.Test;
13 |
14 | /**
15 | * Test for EtcdStorageFactory.
16 | *
17 | * @since 0.1
18 | */
19 | @SuppressWarnings("PMD.AvoidDuplicateLiterals")
20 | public final class EtcdStorageFactoryTest {
21 | @Test
22 | void shouldCreateEtcdStorage() {
23 | MatcherAssert.assertThat(
24 | new StoragesLoader()
25 | .newObject(
26 | "etcd",
27 | new Config.YamlStorageConfig(
28 | Yaml.createYamlMappingBuilder().add(
29 | "connection",
30 | Yaml.createYamlMappingBuilder()
31 | .add(
32 | "endpoints",
33 | Yaml.createYamlSequenceBuilder()
34 | .add("http://localhost")
35 | .build()
36 | )
37 | .build()
38 | )
39 | .build()
40 | )
41 | ),
42 | new IsInstanceOf(EtcdStorage.class)
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/misc/UncheckedScalarTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.ArtipieException;
8 | import com.artipie.asto.ArtipieIOException;
9 | import java.io.IOException;
10 | import org.hamcrest.MatcherAssert;
11 | import org.hamcrest.core.IsEqual;
12 | import org.junit.jupiter.api.Assertions;
13 | import org.junit.jupiter.api.Test;
14 |
15 | /**
16 | * Test for {@link UncheckedScalar} and {@link UncheckedIOScalar}.
17 | * @since 1.3
18 | * @checkstyle LeftCurlyCheck (200 lines)
19 | * @checkstyle AbbreviationAsWordInNameCheck (200 lines)
20 | */
21 | class UncheckedScalarTest {
22 |
23 | @Test
24 | void throwsArtipieException() {
25 | final Exception error = new Exception("Error");
26 | final Exception res = Assertions.assertThrows(
27 | ArtipieException.class,
28 | () -> new UncheckedScalar<>(() -> { throw error; }).value()
29 | );
30 | MatcherAssert.assertThat(
31 | res.getCause(),
32 | new IsEqual<>(error)
33 | );
34 | }
35 |
36 | @Test
37 | void throwsArtipieIOException() {
38 | final IOException error = new IOException("IO error");
39 | final Exception res = Assertions.assertThrows(
40 | ArtipieIOException.class,
41 | () -> new UncheckedIOScalar<>(() -> { throw error; }).value()
42 | );
43 | MatcherAssert.assertThat(
44 | res.getCause(),
45 | new IsEqual<>(error)
46 | );
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/cache/DigestVerification.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.cache;
6 |
7 | import com.artipie.asto.Key;
8 | import com.artipie.asto.ext.ContentDigest;
9 | import java.security.MessageDigest;
10 | import java.util.Arrays;
11 | import java.util.concurrent.CompletableFuture;
12 | import java.util.concurrent.CompletionStage;
13 | import java.util.function.Supplier;
14 |
15 | /**
16 | * By digest verification.
17 | * @since 0.25
18 | */
19 | public final class DigestVerification implements CacheControl {
20 |
21 | /**
22 | * Message digest.
23 | */
24 | private final Supplier digest;
25 |
26 | /**
27 | * Expected digest.
28 | */
29 | private final byte[] expected;
30 |
31 | /**
32 | * New digest verification.
33 | * @param digest Message digest has func
34 | * @param expected Expected digest bytes
35 | */
36 | @SuppressWarnings("PMD.ArrayIsStoredDirectly")
37 | public DigestVerification(final Supplier digest, final byte[] expected) {
38 | this.digest = digest;
39 | this.expected = expected;
40 | }
41 |
42 | @Override
43 | public CompletionStage validate(final Key item, final Remote content) {
44 | return content.get().thenCompose(
45 | val -> val.map(pub -> new ContentDigest(pub, this.digest).bytes())
46 | .orElse(CompletableFuture.completedFuture(new byte[]{}))
47 | ).thenApply(actual -> Arrays.equals(this.expected, actual));
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/cache/RemoteWithErrorHandlingTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.cache;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.FailedCompletionStage;
9 | import com.artipie.asto.ext.PublisherAs;
10 | import java.net.ConnectException;
11 | import java.util.Optional;
12 | import java.util.concurrent.CompletableFuture;
13 | import org.hamcrest.MatcherAssert;
14 | import org.hamcrest.core.IsEqual;
15 | import org.junit.jupiter.api.Test;
16 |
17 | /**
18 | * Test for {@link Remote.WithErrorHandling}.
19 | * @since 0.32
20 | */
21 | class RemoteWithErrorHandlingTest {
22 |
23 | @Test
24 | void returnsContentFromOrigin() {
25 | final byte[] bytes = "123".getBytes();
26 | MatcherAssert.assertThat(
27 | new PublisherAs(
28 | new Remote.WithErrorHandling(
29 | () -> CompletableFuture.completedFuture(
30 | Optional.of(new Content.From(bytes))
31 | )
32 | ).get().toCompletableFuture().join().get()
33 | ).bytes().toCompletableFuture().join(),
34 | new IsEqual<>(bytes)
35 | );
36 | }
37 |
38 | @Test
39 | void returnsEmptyOnError() {
40 | MatcherAssert.assertThat(
41 | new Remote.WithErrorHandling(
42 | () -> new FailedCompletionStage<>(new ConnectException("Connection error"))
43 | ).get().toCompletableFuture().join().isPresent(),
44 | new IsEqual<>(false)
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/misc/UncheckedSupplierTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.ArtipieException;
8 | import com.artipie.asto.ArtipieIOException;
9 | import java.io.IOException;
10 | import org.hamcrest.MatcherAssert;
11 | import org.hamcrest.core.IsEqual;
12 | import org.junit.jupiter.api.Assertions;
13 | import org.junit.jupiter.api.Test;
14 |
15 | /**
16 | * Test for {@link UncheckedSupplier} and {@link UncheckedIOSupplier}.
17 | * @since 1.8
18 | * @checkstyle LeftCurlyCheck (200 lines)
19 | * @checkstyle AbbreviationAsWordInNameCheck (200 lines)
20 | */
21 | class UncheckedSupplierTest {
22 |
23 | @Test
24 | void throwsArtipieException() {
25 | final Exception error = new Exception("Error");
26 | final Exception res = Assertions.assertThrows(
27 | ArtipieException.class,
28 | () -> new UncheckedSupplier<>(() -> { throw error; }).get()
29 | );
30 | MatcherAssert.assertThat(
31 | res.getCause(),
32 | new IsEqual<>(error)
33 | );
34 | }
35 |
36 | @Test
37 | void throwsArtipieIOException() {
38 | final IOException error = new IOException("IO error");
39 | final Exception res = Assertions.assertThrows(
40 | ArtipieIOException.class,
41 | () -> new UncheckedIOSupplier<>(() -> { throw error; }).get()
42 | );
43 | MatcherAssert.assertThat(
44 | res.getCause(),
45 | new IsEqual<>(error)
46 | );
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/misc/UncheckedFuncTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.ArtipieException;
8 | import com.artipie.asto.ArtipieIOException;
9 | import java.io.IOException;
10 | import org.hamcrest.MatcherAssert;
11 | import org.hamcrest.core.IsEqual;
12 | import org.junit.jupiter.api.Assertions;
13 | import org.junit.jupiter.api.Test;
14 |
15 | /**
16 | * Test for {@link UncheckedFunc} and {@link UncheckedIOFunc}.
17 | * @since 1.1
18 | * @checkstyle LeftCurlyCheck (200 lines)
19 | * @checkstyle AbbreviationAsWordInNameCheck (200 lines)
20 | */
21 | class UncheckedFuncTest {
22 |
23 | @Test
24 | void throwsArtipieException() {
25 | final Exception error = new Exception("Error");
26 | final Exception res = Assertions.assertThrows(
27 | ArtipieException.class,
28 | () -> new UncheckedFunc<>(ignored -> { throw error; }).apply("ignored")
29 | );
30 | MatcherAssert.assertThat(
31 | res.getCause(),
32 | new IsEqual<>(error)
33 | );
34 | }
35 |
36 | @Test
37 | void throwsArtipieIOException() {
38 | final IOException error = new IOException("IO error");
39 | final Exception res = Assertions.assertThrows(
40 | ArtipieIOException.class,
41 | () -> new UncheckedIOFunc<>(ignored -> { throw error; }).apply("nothing")
42 | );
43 | MatcherAssert.assertThat(
44 | res.getCause(),
45 | new IsEqual<>(error)
46 | );
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/ext/CompletableFutureSupport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 |
6 | package com.artipie.asto.ext;
7 |
8 | import java.util.concurrent.CompletableFuture;
9 | import java.util.function.Supplier;
10 |
11 | /**
12 | * Support of new {@link CompletableFuture} API for JDK 1.8.
13 | * @param Future type
14 | * @since 0.33
15 | */
16 | public abstract class CompletableFutureSupport implements Supplier> {
17 |
18 | /**
19 | * Supplier wrap.
20 | */
21 | private final Supplier extends CompletableFuture> wrap;
22 |
23 | /**
24 | * New wrapped future supplier.
25 | * @param wrap Supplier to wrap
26 | */
27 | protected CompletableFutureSupport(final Supplier extends CompletableFuture> wrap) {
28 | this.wrap = wrap;
29 | }
30 |
31 | @Override
32 | public final CompletableFuture get() {
33 | return this.wrap.get();
34 | }
35 |
36 | /**
37 | * Failed completable future supplier.
38 | * @param Future type
39 | * @since 0.33
40 | */
41 | public static final class Failed extends CompletableFutureSupport {
42 | /**
43 | * New failed future.
44 | * @param err Failure exception
45 | */
46 | public Failed(final Exception err) {
47 | super(() -> {
48 | final CompletableFuture future = new CompletableFuture<>();
49 | future.completeExceptionally(err);
50 | return future;
51 | });
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/misc/UncheckedConsumerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.ArtipieException;
8 | import com.artipie.asto.ArtipieIOException;
9 | import java.io.IOException;
10 | import org.hamcrest.MatcherAssert;
11 | import org.hamcrest.core.IsEqual;
12 | import org.junit.jupiter.api.Assertions;
13 | import org.junit.jupiter.api.Test;
14 |
15 | /**
16 | * Test for {@link UncheckedConsumer} and {@link UncheckedIOConsumer}.
17 | * @since 1.1
18 | * @checkstyle LeftCurlyCheck (200 lines)
19 | * @checkstyle AbbreviationAsWordInNameCheck (200 lines)
20 | */
21 | class UncheckedConsumerTest {
22 |
23 | @Test
24 | void throwsArtipieException() {
25 | final Exception error = new Exception("Error");
26 | final Exception res = Assertions.assertThrows(
27 | ArtipieException.class,
28 | () -> new UncheckedConsumer<>(ignored -> { throw error; }).accept("ignored")
29 | );
30 | MatcherAssert.assertThat(
31 | res.getCause(),
32 | new IsEqual<>(error)
33 | );
34 | }
35 |
36 | @Test
37 | void throwsArtipieIOException() {
38 | final IOException error = new IOException("IO error");
39 | final Exception res = Assertions.assertThrows(
40 | ArtipieIOException.class,
41 | () -> new UncheckedIOConsumer<>(ignored -> { throw error; }).accept("nothing")
42 | );
43 | MatcherAssert.assertThat(
44 | res.getCause(),
45 | new IsEqual<>(error)
46 | );
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/key/KeyExcludeLast.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 |
6 | package com.artipie.asto.key;
7 |
8 | import com.artipie.asto.Key;
9 | import java.util.LinkedList;
10 | import java.util.List;
11 |
12 | /**
13 | * Key that excludes the last occurrence of a part.
14 | * @implNote If part to exclude was not found, the class can return the origin key.
15 | * @since 1.9.1
16 | */
17 | public final class KeyExcludeLast extends Key.Wrap {
18 |
19 | /**
20 | * Ctor.
21 | * @param key Key
22 | * @param part Part to exclude
23 | */
24 | public KeyExcludeLast(final Key key, final String part) {
25 | super(
26 | new From(KeyExcludeLast.exclude(key, part))
27 | );
28 | }
29 |
30 | /**
31 | * Excludes last occurrence of part.
32 | * @param key Key
33 | * @param part Part to exclude
34 | * @return List of parts
35 | */
36 | private static List exclude(final Key key, final String part) {
37 | final List allparts = key.parts();
38 | int ifound = -1;
39 | for (int ind = allparts.size() - 1; ind >= 0; ind = ind - 1) {
40 | final String prt = allparts.get(ind);
41 | if (prt.equals(part)) {
42 | ifound = ind;
43 | break;
44 | }
45 | }
46 | final List parts = new LinkedList<>();
47 | for (int ind = 0; ind < allparts.size(); ind = ind + 1) {
48 | if (ind != ifound) {
49 | parts.add(allparts.get(ind));
50 | }
51 | }
52 | return parts;
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/misc/UncheckedFunc.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.ArtipieException;
8 | import java.util.function.Function;
9 |
10 | /**
11 | * Unchecked {@link Function}.
12 | * @param Function type
13 | * @param Function return type
14 | * @param Error type
15 | * @since 1.1
16 | */
17 | public final class UncheckedFunc implements Function {
18 |
19 | /**
20 | * Checked version.
21 | */
22 | private final Checked checked;
23 |
24 | /**
25 | * Ctor.
26 | * @param checked Checked func
27 | */
28 | public UncheckedFunc(final Checked checked) {
29 | this.checked = checked;
30 | }
31 |
32 | @Override
33 | @SuppressWarnings("PMD.AvoidCatchingGenericException")
34 | public R apply(final T val) {
35 | try {
36 | return this.checked.apply(val);
37 | // @checkstyle IllegalCatchCheck (1 line)
38 | } catch (final Exception err) {
39 | throw new ArtipieException(err);
40 | }
41 | }
42 |
43 | /**
44 | * Checked version of consumer.
45 | * @param Consumer type
46 | * @param Return type
47 | * @param Error type
48 | * @since 1.1
49 | */
50 | @FunctionalInterface
51 | public interface Checked {
52 |
53 | /**
54 | * Apply value.
55 | * @param value Value to accept
56 | * @return Result
57 | * @throws E On error
58 | */
59 | R apply(T value) throws E;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/misc/UncheckedSupplier.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.ArtipieException;
8 | import java.util.function.Supplier;
9 |
10 | /**
11 | * Supplier to wrap checked supplier throwing checked exception
12 | * with unchecked one.
13 | * @param Supplier type
14 | * @since 1.8
15 | */
16 | public final class UncheckedSupplier implements Supplier {
17 |
18 | /**
19 | * Supplier which throws checked exceptions.
20 | */
21 | private final CheckedSupplier extends T, ? extends Exception> checked;
22 |
23 | /**
24 | * Wrap checked supplier with unchecked.
25 | * @param checked Checked supplier
26 | */
27 | public UncheckedSupplier(final CheckedSupplier checked) {
28 | this.checked = checked;
29 | }
30 |
31 | @Override
32 | @SuppressWarnings("PMD.AvoidCatchingGenericException")
33 | public T get() {
34 | try {
35 | return this.checked.get();
36 | // @checkstyle IllegalCatchCheck (1 line)
37 | } catch (final Exception err) {
38 | throw new ArtipieException(err);
39 | }
40 | }
41 |
42 | /**
43 | * Checked supplier which throws exception.
44 | * @param Supplier type
45 | * @param Exception type
46 | * @since 1.0
47 | */
48 | @FunctionalInterface
49 | public interface CheckedSupplier {
50 |
51 | /**
52 | * Get value or throw exception.
53 | * @return Value
54 | * @throws Exception of type E
55 | */
56 | T get() throws E;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/asto-s3/src/test/java/com/artipie/asto/s3/EstimatedContentTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.s3;
6 |
7 | import com.artipie.asto.Content;
8 | import java.nio.charset.StandardCharsets;
9 | import java.util.Optional;
10 | import java.util.concurrent.ExecutionException;
11 | import org.hamcrest.MatcherAssert;
12 | import org.hamcrest.core.IsEqual;
13 | import org.junit.jupiter.api.Test;
14 |
15 | /**
16 | * Test for {@link EstimatedContentCompliment}.
17 | *
18 | * @since 0.1
19 | */
20 | final class EstimatedContentTest {
21 | @Test
22 | void shouldReadUntilLimit() throws ExecutionException, InterruptedException {
23 | final byte[] data = "xxx".getBytes(StandardCharsets.UTF_8);
24 | final Content content = new EstimatedContentCompliment(
25 | new Content.From(
26 | Optional.empty(),
27 | new Content.From(data)
28 | ),
29 | 1
30 | ).estimate().toCompletableFuture().get();
31 | MatcherAssert.assertThat(
32 | content.size(), new IsEqual<>(Optional.empty())
33 | );
34 | }
35 |
36 | @Test
37 | void shouldEvaluateSize() throws ExecutionException, InterruptedException {
38 | final byte[] data = "yyy".getBytes(StandardCharsets.UTF_8);
39 | final Content content = new EstimatedContentCompliment(
40 | new Content.From(
41 | Optional.empty(),
42 | new Content.From(data)
43 | )
44 | ).estimate().toCompletableFuture().get();
45 | MatcherAssert.assertThat(
46 | content.size(), new IsEqual<>(Optional.of((long) data.length))
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/Concatenation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import io.reactivex.Flowable;
8 | import io.reactivex.Single;
9 | import java.nio.ByteBuffer;
10 | import org.reactivestreams.Publisher;
11 |
12 | /**
13 | * Concatenation of {@link ByteBuffer} instances.
14 | *
15 | * @since 0.17
16 | */
17 | public class Concatenation {
18 |
19 | /**
20 | * Source of byte buffers.
21 | */
22 | private final Publisher source;
23 |
24 | /**
25 | * Ctor.
26 | *
27 | * @param source Source of byte buffers.
28 | */
29 | public Concatenation(final Publisher source) {
30 | this.source = source;
31 | }
32 |
33 | /**
34 | * Concatenates all buffers into single one.
35 | *
36 | * @return Single buffer.
37 | */
38 | public Single single() {
39 | return Flowable.fromPublisher(this.source).reduce(
40 | ByteBuffer.allocate(0),
41 | (left, right) -> {
42 | right.mark();
43 | final ByteBuffer result;
44 | if (left.capacity() - left.limit() >= right.limit()) {
45 | left.position(left.limit());
46 | left.limit(left.limit() + right.limit());
47 | result = left.put(right);
48 | } else {
49 | result = ByteBuffer.allocate(
50 | 2 * Math.max(left.capacity(), right.capacity())
51 | ).put(left).put(right);
52 | }
53 | right.reset();
54 | result.flip();
55 | return result;
56 | }
57 | );
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/ext/ContentAs.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.ext;
6 |
7 | import io.reactivex.Single;
8 | import io.reactivex.functions.Function;
9 | import java.nio.ByteBuffer;
10 | import java.nio.charset.StandardCharsets;
11 | import org.reactivestreams.Publisher;
12 |
13 | /**
14 | * Rx publisher transformer to single.
15 | * @param Single type
16 | * @since 0.33
17 | */
18 | public final class ContentAs
19 | implements Function>, Single extends T>> {
20 |
21 | /**
22 | * Content as string.
23 | */
24 | public static final ContentAs STRING = new ContentAs<>(
25 | bytes -> new String(bytes, StandardCharsets.UTF_8)
26 | );
27 |
28 | /**
29 | * Content as {@code long} number.
30 | */
31 | public static final ContentAs LONG = new ContentAs<>(
32 | bytes -> Long.valueOf(new String(bytes, StandardCharsets.US_ASCII))
33 | );
34 |
35 | /**
36 | * Content as {@code bytes}.
37 | */
38 | public static final ContentAs BYTES = new ContentAs<>(bytes -> bytes);
39 |
40 | /**
41 | * Transform function.
42 | */
43 | private final Function transform;
44 |
45 | /**
46 | * Ctor.
47 | * @param transform Transform function
48 | */
49 | public ContentAs(final Function transform) {
50 | this.transform = transform;
51 | }
52 |
53 | @Override
54 | public Single extends T> apply(
55 | final Single extends Publisher> content
56 | ) {
57 | return content.flatMap(
58 | pub -> Single.fromFuture(new PublisherAs(pub).bytes().toCompletableFuture())
59 | ).map(this.transform);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/OneTimePublisher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import java.util.concurrent.atomic.AtomicInteger;
8 | import org.reactivestreams.Publisher;
9 | import org.reactivestreams.Subscriber;
10 | import org.reactivestreams.Subscription;
11 |
12 | /**
13 | * A publish which can be consumed only once.
14 | * @param The type of publisher elements.
15 | * @since 0.23
16 | */
17 | @SuppressWarnings("PMD.UncommentedEmptyMethodBody")
18 | public final class OneTimePublisher implements Publisher {
19 |
20 | /**
21 | * The original publisher.
22 | */
23 | private final Publisher original;
24 |
25 | /**
26 | * The amount of subscribers.
27 | */
28 | private final AtomicInteger subscribers;
29 |
30 | /**
31 | * Wrap a publish in a way it can be used only once.
32 | * @param original The original publisher.
33 | */
34 | public OneTimePublisher(final Publisher original) {
35 | this.original = original;
36 | this.subscribers = new AtomicInteger(0);
37 | }
38 |
39 | @Override
40 | public void subscribe(final Subscriber super T> sub) {
41 | final int subs = this.subscribers.incrementAndGet();
42 | if (subs == 1) {
43 | this.original.subscribe(sub);
44 | } else {
45 | final String msg =
46 | "The subscriber could not be consumed more than once. Failed on #%d attempt";
47 | sub.onSubscribe(
48 | new Subscription() {
49 | @Override
50 | public void request(final long cnt) {
51 | }
52 |
53 | @Override
54 | public void cancel() {
55 | }
56 | }
57 | );
58 | sub.onError(new ArtipieIOException(String.format(msg, subs)));
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/misc/UncheckedRunnable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.misc;
6 |
7 | import com.artipie.asto.ArtipieIOException;
8 | import java.io.IOException;
9 |
10 | /**
11 | * Unchecked {@link Runnable}.
12 | *
13 | * @since 1.12
14 | */
15 | public final class UncheckedRunnable implements Runnable {
16 | /**
17 | * Original runnable.
18 | */
19 | private final CheckedRunnable extends Exception> original;
20 |
21 | /**
22 | * Ctor.
23 | *
24 | * @param original Original runnable.
25 | */
26 | public UncheckedRunnable(final CheckedRunnable extends Exception> original) {
27 | this.original = original;
28 | }
29 |
30 | /**
31 | * New {@code UncheckedRunnable}.
32 | *
33 | * @param original Runnable, that can throw {@code IOException}
34 | * @param An error
35 | * @return UncheckedRunnable
36 | */
37 | @SuppressWarnings("PMD.ProhibitPublicStaticMethods")
38 | public static UncheckedRunnable newIoRunnable(
39 | final CheckedRunnable original
40 | ) {
41 | return new UncheckedRunnable(original);
42 | }
43 |
44 | @Override
45 | @SuppressWarnings("PMD.AvoidCatchingGenericException")
46 | public void run() {
47 | try {
48 | this.original.run();
49 | // @checkstyle IllegalCatchCheck (1 line)
50 | } catch (final Exception err) {
51 | throw new ArtipieIOException(err);
52 | }
53 | }
54 |
55 | /**
56 | * Checked version of runnable.
57 | *
58 | * @param Checked exception.
59 | * @since 1.12
60 | */
61 | @FunctionalInterface
62 | public interface CheckedRunnable {
63 | /**
64 | * Run action.
65 | *
66 | * @throws E An error.
67 | */
68 | void run() throws E;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/asto-vertx-file/src/test/java/com/artipie/asto/VertxFileStorageVerificationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import com.artipie.asto.fs.VertxFileStorage;
8 | import com.artipie.asto.test.StorageWhiteboxVerification;
9 | import io.vertx.reactivex.core.Vertx;
10 | import java.nio.file.Path;
11 | import java.util.Optional;
12 | import org.junit.jupiter.api.AfterAll;
13 | import org.junit.jupiter.api.io.TempDir;
14 |
15 | /**
16 | * Vertx file storage verification test.
17 | *
18 | * @checkstyle ProtectedMethodInFinalClassCheck (500 lines)
19 | * @since 0.1
20 | */
21 | @SuppressWarnings("PMD.TestClassWithoutTestCases")
22 | public final class VertxFileStorageVerificationTest extends StorageWhiteboxVerification {
23 |
24 | /**
25 | * Vert.x file System.
26 | */
27 | private static final Vertx VERTX = Vertx.vertx();
28 |
29 | /**
30 | * Temp dir.
31 | */
32 | @TempDir
33 | private Path temp;
34 |
35 | @Override
36 | protected Storage newStorage() throws Exception {
37 | return new VertxFileStorage(
38 | this.temp.resolve("base"),
39 | VertxFileStorageVerificationTest.VERTX
40 | );
41 | }
42 |
43 | @Override
44 | protected Optional newBaseForRootSubStorage() {
45 | return Optional.of(
46 | new VertxFileStorage(
47 | this.temp.resolve("root-sub-storage"), VertxFileStorageVerificationTest.VERTX
48 | )
49 | );
50 | }
51 |
52 | @Override
53 | protected Optional newBaseForSubStorage() {
54 | return Optional.of(
55 | new VertxFileStorage(
56 | this.temp.resolve("sub-storage"), VertxFileStorageVerificationTest.VERTX
57 | )
58 | );
59 | }
60 |
61 | @AfterAll
62 | static void tearDown() throws Exception {
63 | VertxFileStorageVerificationTest.VERTX.close();
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/fs/FileMetaTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.fs;
6 |
7 | import com.artipie.asto.Meta;
8 | import java.nio.file.attribute.BasicFileAttributes;
9 | import java.nio.file.attribute.FileTime;
10 | import java.time.Instant;
11 | import org.hamcrest.MatcherAssert;
12 | import org.hamcrest.core.IsEqual;
13 | import org.junit.jupiter.api.Test;
14 | import org.mockito.Mockito;
15 |
16 | /**
17 | * Test case for {@link FileMeta}.
18 | * @since 1.9
19 | */
20 | final class FileMetaTest {
21 |
22 | @Test
23 | void readAttrs() {
24 | final long len = 4;
25 | final Instant creation = Instant.ofEpochMilli(1);
26 | final Instant modified = Instant.ofEpochMilli(2);
27 | final Instant access = Instant.ofEpochMilli(3);
28 | final BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class);
29 | Mockito.when(attrs.size()).thenReturn(len);
30 | Mockito.when(attrs.creationTime()).thenReturn(FileTime.from(creation));
31 | Mockito.when(attrs.lastModifiedTime()).thenReturn(FileTime.from(modified));
32 | Mockito.when(attrs.lastAccessTime()).thenReturn(FileTime.from(access));
33 | MatcherAssert.assertThat(
34 | "size",
35 | new FileMeta(attrs).read(Meta.OP_SIZE).get(),
36 | new IsEqual<>(len)
37 | );
38 | MatcherAssert.assertThat(
39 | "created at",
40 | new FileMeta(attrs).read(Meta.OP_CREATED_AT).get(),
41 | new IsEqual<>(creation)
42 | );
43 | MatcherAssert.assertThat(
44 | "updated at",
45 | new FileMeta(attrs).read(Meta.OP_UPDATED_AT).get(),
46 | new IsEqual<>(modified)
47 | );
48 | MatcherAssert.assertThat(
49 | "accessed at",
50 | new FileMeta(attrs).read(Meta.OP_ACCESSED_AT).get(),
51 | new IsEqual<>(access)
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/asto-etcd/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 | asto
28 | com.artipie
29 | 1.0-SNAPSHOT
30 |
31 | 4.0.0
32 | asto-etcd
33 |
34 |
35 | com.artipie
36 | asto-core
37 | 1.0-SNAPSHOT
38 | compile
39 |
40 |
41 |
42 | io.etcd
43 | jetcd-core
44 | 0.7.1
45 | true
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/streams/ContentAsInputStreamTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.streams;
6 |
7 | import com.artipie.asto.Content;
8 | import io.reactivex.Flowable;
9 | import java.io.BufferedReader;
10 | import java.io.InputStream;
11 | import java.io.InputStreamReader;
12 | import java.nio.ByteBuffer;
13 | import java.nio.charset.StandardCharsets;
14 | import org.hamcrest.MatcherAssert;
15 | import org.hamcrest.core.IsEqual;
16 | import org.junit.jupiter.api.Test;
17 |
18 | /**
19 | * Tests for {@link StorageValuePipeline.PublishingOutputStream}.
20 | *
21 | * @since 1.12
22 | * @checkstyle MagicNumberCheck (500 lines)
23 | */
24 | public final class ContentAsInputStreamTest {
25 |
26 | @Test
27 | void shouldGetContentDataFromInputStream() throws Exception {
28 | final StorageValuePipeline.ContentAsInputStream cnt =
29 | new StorageValuePipeline.ContentAsInputStream(
30 | new Content.From(
31 | Flowable.fromArray(
32 | ByteBuffer.wrap("test data".getBytes(StandardCharsets.UTF_8)),
33 | ByteBuffer.wrap(" test data2".getBytes(StandardCharsets.UTF_8))
34 | )
35 | )
36 | );
37 | try (BufferedReader in = new BufferedReader(new InputStreamReader(cnt.inputStream()))) {
38 | MatcherAssert.assertThat(
39 | in.readLine(),
40 | new IsEqual<>("test data test data2")
41 | );
42 | }
43 | }
44 |
45 | @Test
46 | void shouldEndOfStreamWhenContentIsEmpty() throws Exception {
47 | final StorageValuePipeline.ContentAsInputStream cnt =
48 | new StorageValuePipeline.ContentAsInputStream(Content.EMPTY);
49 | try (InputStream stream = cnt.inputStream()) {
50 | final byte[] buf = new byte[8];
51 | MatcherAssert.assertThat(
52 | stream.read(buf),
53 | new IsEqual<>(-1)
54 | );
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/Splitting.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import io.reactivex.Flowable;
8 | import java.nio.ByteBuffer;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 | import org.reactivestreams.Publisher;
12 |
13 | /**
14 | * Splits the original ByteBuffer to several ones
15 | * with size less or equals defined max size.
16 | *
17 | * @since 1.12.0
18 | */
19 | public class Splitting {
20 |
21 | /**
22 | * Source byte buffer.
23 | */
24 | private final ByteBuffer source;
25 |
26 | /**
27 | * Max size of split byte buffer.
28 | */
29 | private final int size;
30 |
31 | /**
32 | * Ctor.
33 | *
34 | * @param source Source byte buffer.
35 | * @param size Max size of split byte buffer.
36 | */
37 | public Splitting(final ByteBuffer source, final int size) {
38 | this.source = source;
39 | this.size = size;
40 | }
41 |
42 | /**
43 | * Splits the original ByteBuffer to ones with size less
44 | * or equals defined max {@code size}.
45 | *
46 | * @return Publisher of ByteBuffers.
47 | */
48 | public Publisher publisher() {
49 | final Publisher res;
50 | int remaining = this.source.remaining();
51 | if (remaining > this.size) {
52 | final List parts = new ArrayList<>(remaining / this.size + 1);
53 | while (remaining > 0) {
54 | final byte[] bytes;
55 | if (remaining > this.size) {
56 | bytes = new byte[this.size];
57 | } else {
58 | bytes = new byte[remaining];
59 | }
60 | this.source.get(bytes);
61 | parts.add(ByteBuffer.wrap(bytes));
62 | remaining = this.source.remaining();
63 | }
64 | res = Flowable.fromIterable(parts);
65 | } else {
66 | res = Flowable.just(this.source);
67 | }
68 | return res;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/asto-etcd/src/test/java/com/artipie/asto/etcd/EtcdStorageVerificationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.etcd;
6 |
7 | import com.artipie.asto.Storage;
8 | import com.artipie.asto.test.StorageWhiteboxVerification;
9 | import io.etcd.jetcd.Client;
10 | import io.etcd.jetcd.test.EtcdClusterExtension;
11 | import java.net.URI;
12 | import java.util.List;
13 | import java.util.Optional;
14 | import java.util.stream.Collectors;
15 | import org.junit.jupiter.api.AfterAll;
16 | import org.junit.jupiter.api.BeforeAll;
17 | import org.junit.jupiter.api.condition.DisabledOnOs;
18 | import org.junit.jupiter.api.condition.OS;
19 |
20 | /**
21 | * ETCD storage verification test.
22 | *
23 | * @checkstyle ProtectedMethodInFinalClassCheck (500 lines)
24 | * @since 0.1
25 | */
26 | @SuppressWarnings("PMD.TestClassWithoutTestCases")
27 | @DisabledOnOs(OS.WINDOWS)
28 | public final class EtcdStorageVerificationTest extends StorageWhiteboxVerification {
29 | /**
30 | * Etcd cluster.
31 | */
32 | private static EtcdClusterExtension etcd;
33 |
34 | @Override
35 | protected Storage newStorage() {
36 | final List endpoints = EtcdStorageVerificationTest.etcd.getClientEndpoints();
37 | return new EtcdStorage(
38 | Client.builder().endpoints(endpoints).build(),
39 | endpoints.stream().map(URI::toString).collect(Collectors.joining())
40 | );
41 | }
42 |
43 | @Override
44 | protected Optional newBaseForRootSubStorage() {
45 | return Optional.empty();
46 | }
47 |
48 | @BeforeAll
49 | static void beforeClass() throws Exception {
50 | EtcdStorageVerificationTest.etcd = new EtcdClusterExtension(
51 | "test-etcd",
52 | 1,
53 | false,
54 | "--data-dir",
55 | "/data.etcd0"
56 | );
57 | EtcdStorageVerificationTest.etcd.beforeAll(null);
58 | }
59 |
60 | @AfterAll
61 | static void afterClass() throws Exception {
62 | EtcdStorageVerificationTest.etcd.afterAll(null);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/memory/BenchmarkStorageExistsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.memory;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.Key;
9 | import java.util.NavigableMap;
10 | import java.util.TreeMap;
11 | import org.hamcrest.MatcherAssert;
12 | import org.hamcrest.core.IsEqual;
13 | import org.junit.jupiter.api.Test;
14 |
15 | /**
16 | * Tests for {@link BenchmarkStorage#exists(Key)}.
17 | * @since 1.2.0
18 | */
19 | @SuppressWarnings("PMD.AvoidDuplicateLiterals")
20 | final class BenchmarkStorageExistsTest {
21 | @Test
22 | void existsWhenPresentInLocalAndNotDeleted() {
23 | final InMemoryStorage memory = new InMemoryStorage();
24 | final BenchmarkStorage bench = new BenchmarkStorage(memory);
25 | final Key key = new Key.From("somekey");
26 | bench.save(key, Content.EMPTY).join();
27 | MatcherAssert.assertThat(
28 | bench.exists(key).join(),
29 | new IsEqual<>(true)
30 | );
31 | }
32 |
33 | @Test
34 | void existsWhenPresentInBackendAndNotDeleted() {
35 | final Key key = new Key.From("somekey");
36 | final NavigableMap backdata = new TreeMap<>();
37 | backdata.put(key.string(), "shouldExist".getBytes());
38 | final InMemoryStorage memory = new InMemoryStorage(backdata);
39 | final BenchmarkStorage bench = new BenchmarkStorage(memory);
40 | MatcherAssert.assertThat(
41 | bench.exists(key).join(),
42 | new IsEqual<>(true)
43 | );
44 | }
45 |
46 | @Test
47 | void notExistsIfKeyWasDeleted() {
48 | final InMemoryStorage memory = new InMemoryStorage();
49 | final BenchmarkStorage bench = new BenchmarkStorage(memory);
50 | final Key key = new Key.From("somekey");
51 | bench.save(key, new Content.From("any data".getBytes())).join();
52 | bench.delete(key).join();
53 | MatcherAssert.assertThat(
54 | bench.exists(key).join(),
55 | new IsEqual<>(false)
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Create Maven release
2 | on:
3 | push:
4 | tags:
5 | - 'v*'
6 | jobs:
7 | build:
8 | name: Build release
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2.3.3
12 | - uses: actions/setup-java@v2
13 | with:
14 | java-version: 8
15 | distribution: adopt
16 | - uses: actions/cache@v2
17 | with:
18 | path: ~/.m2/repository
19 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
20 | restore-keys: |
21 | ${{ runner.os }}-maven-
22 | - name: Import GPG key
23 | uses: crazy-max/ghaction-import-gpg@v3
24 | with:
25 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
26 | passphrase: ${{ secrets.GPG_PASSPHRASE }}
27 | - name: Set env
28 | run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
29 | - name: Set version
30 | run: mvn -B versions:set -DnewVersion=${{ env.TAG }} versions:commit
31 | - name: Create settings.xml
32 | uses: whelk-io/maven-settings-xml-action@v15
33 | with:
34 | servers: |
35 | [
36 | {
37 | "id": "oss.sonatype.org",
38 | "username": "${{ secrets.SONATYPE_USER }}",
39 | "password": "${{ secrets.SONATYPE_PASSWORD }}"
40 | }
41 | ]
42 | profiles: |
43 | [
44 | {
45 | "id": "artipie",
46 | "properties": {
47 | "gpg.keyname": "${{ secrets.GPG_KEYNAME }}",
48 | "gpg.passphrase": "${{ secrets.GPG_PASSPHRASE }}"
49 | }
50 | }
51 | ]
52 | - name: Deploy artifacts
53 | run: mvn deploy -Partipie,publish,sonatype,gpg-sign -DskipITs --errors
54 | - name: Create Github Release
55 | id: create_release
56 | uses: actions/create-release@v1
57 | env:
58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
59 | with:
60 | tag_name: ${{ github.ref }}
61 | release_name: Release ${{ env.TAG }}
62 | draft: false
63 | prerelease: false
64 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/test/ContentIs.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.test;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.ext.PublisherAs;
9 | import com.google.common.util.concurrent.Uninterruptibles;
10 | import java.nio.charset.Charset;
11 | import java.util.concurrent.ExecutionException;
12 | import org.hamcrest.Description;
13 | import org.hamcrest.Matcher;
14 | import org.hamcrest.Matchers;
15 | import org.hamcrest.TypeSafeMatcher;
16 |
17 | /**
18 | * Matcher for {@link Content}.
19 | * @since 0.24
20 | */
21 | public final class ContentIs extends TypeSafeMatcher {
22 |
23 | /**
24 | * Byte array matcher.
25 | */
26 | private final Matcher matcher;
27 |
28 | /**
29 | * Content is a string with encoding.
30 | * @param expected String
31 | * @param enc Encoding charset
32 | */
33 | public ContentIs(final String expected, final Charset enc) {
34 | this(expected.getBytes(enc));
35 | }
36 |
37 | /**
38 | * Content is a byte array.
39 | * @param expected Byte array
40 | */
41 | public ContentIs(final byte[] expected) {
42 | this(Matchers.equalTo(expected));
43 | }
44 |
45 | /**
46 | * Content matches for byte array matcher.
47 | * @param matcher Byte array matcher
48 | */
49 | public ContentIs(final Matcher matcher) {
50 | this.matcher = matcher;
51 | }
52 |
53 | @Override
54 | public void describeTo(final Description description) {
55 | description.appendText("has bytes ").appendValue(this.matcher);
56 | }
57 |
58 | @Override
59 | public boolean matchesSafely(final Content item) {
60 | try {
61 | return this.matcher.matches(
62 | Uninterruptibles.getUninterruptibly(
63 | new PublisherAs(item).bytes().toCompletableFuture()
64 | )
65 | );
66 | } catch (final ExecutionException err) {
67 | throw new IllegalStateException("Failed to read content", err);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/asto-artipie/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 | asto
28 | com.artipie
29 | 1.0-SNAPSHOT
30 |
31 | 4.0.0
32 | asto-artipie
33 |
34 |
35 | com.artipie
36 | asto-core
37 | 1.0-SNAPSHOT
38 | compile
39 |
40 |
41 | com.artipie
42 | http-client
43 | 0.3.9
44 |
45 |
46 | com.artipie
47 | asto-core
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/ext/Digests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.ext;
6 |
7 | import java.security.MessageDigest;
8 | import java.security.NoSuchAlgorithmException;
9 | import java.util.function.Supplier;
10 | import java.util.stream.Stream;
11 |
12 | /**
13 | * Common digests.
14 | * @since 0.22
15 | */
16 | public enum Digests implements Supplier {
17 | /**
18 | * Common digest algorithms.
19 | */
20 | SHA256("SHA-256"), SHA1("SHA-1"), MD5("MD5"), SHA512("SHA-512");
21 |
22 | /**
23 | * Digest name.
24 | */
25 | private final String name;
26 |
27 | /**
28 | * New digest for name.
29 | * @param name Digest name
30 | */
31 | Digests(final String name) {
32 | this.name = name;
33 | }
34 |
35 | @Override
36 | public MessageDigest get() {
37 | try {
38 | return MessageDigest.getInstance(this.name);
39 | } catch (final NoSuchAlgorithmException err) {
40 | throw new IllegalStateException(String.format("No algorithm '%s'", this.name), err);
41 | }
42 | }
43 |
44 | /**
45 | * Digest enum item from string digest algorithm, case insensitive.
46 | * @since 0.24
47 | */
48 | public static final class FromString {
49 |
50 | /**
51 | * Algorithm string representation.
52 | */
53 | private final String from;
54 |
55 | /**
56 | * Ctor.
57 | * @param from Algorithm string representation
58 | */
59 | public FromString(final String from) {
60 | this.from = from;
61 | }
62 |
63 | /**
64 | * Returns {@link Digests} enum item.
65 | * @return Digest
66 | */
67 | public Digests get() {
68 | return Stream.of(Digests.values()).filter(
69 | digest -> digest.name.equalsIgnoreCase(this.from)
70 | ).findFirst().orElseThrow(
71 | () -> new IllegalArgumentException(
72 | String.format("Unsupported digest algorithm %s", this.from)
73 | )
74 | );
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/cache/DigestVerificationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.cache;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.Key;
9 | import com.artipie.asto.ext.Digests;
10 | import java.util.Optional;
11 | import java.util.concurrent.CompletableFuture;
12 | import org.apache.commons.codec.binary.Hex;
13 | import org.hamcrest.MatcherAssert;
14 | import org.hamcrest.Matchers;
15 | import org.junit.jupiter.api.Test;
16 |
17 | /**
18 | * Test case for {@link DigestVerification}.
19 | *
20 | * @since 0.25
21 | * @checkstyle MagicNumberCheck (500 lines)
22 | */
23 | final class DigestVerificationTest {
24 |
25 | @Test
26 | void validatesCorrectDigest() throws Exception {
27 | final boolean result = new DigestVerification(
28 | Digests.MD5,
29 | Hex.decodeHex("5289df737df57326fcdd22597afb1fac")
30 | ).validate(
31 | new Key.From("any"),
32 | () -> CompletableFuture.supplyAsync(
33 | () -> Optional.of(new Content.From(new byte[]{1, 2, 3}))
34 | )
35 | ).toCompletableFuture().get();
36 | MatcherAssert.assertThat(result, Matchers.is(true));
37 | }
38 |
39 | @Test
40 | void doesntValidatesIncorrectDigest() throws Exception {
41 | final boolean result = new DigestVerification(
42 | Digests.MD5, new byte[16]
43 | ).validate(
44 | new Key.From("other"),
45 | () -> CompletableFuture.supplyAsync(
46 | () -> Optional.of(new Content.From(new byte[]{1, 2, 3}))
47 | )
48 | ).toCompletableFuture().get();
49 | MatcherAssert.assertThat(result, Matchers.is(false));
50 | }
51 |
52 | @Test
53 | void doesntValidateAbsentContent() throws Exception {
54 | MatcherAssert.assertThat(
55 | new DigestVerification(
56 | Digests.MD5, new byte[16]
57 | ).validate(
58 | new Key.From("something"),
59 | () -> CompletableFuture.supplyAsync(Optional::empty)
60 | ).toCompletableFuture().get(),
61 | Matchers.is(false)
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/cache/FromRemoteCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.cache;
6 |
7 | import com.artipie.asto.ArtipieIOException;
8 | import com.artipie.asto.Content;
9 | import com.artipie.asto.Key;
10 | import com.artipie.asto.Storage;
11 | import java.util.Optional;
12 | import java.util.concurrent.CompletionStage;
13 | import java.util.function.Function;
14 |
15 | /**
16 | * This cache implementation loads all the items from remote and caches it to storage. Content
17 | * is loaded from cache only if remote failed to return requested item.
18 | * @since 0.30
19 | */
20 | public final class FromRemoteCache implements Cache {
21 |
22 | /**
23 | * Back-end storage.
24 | */
25 | private final Storage storage;
26 |
27 | /**
28 | * New remote cache.
29 | * @param storage Back-end storage for cache
30 | */
31 | public FromRemoteCache(final Storage storage) {
32 | this.storage = storage;
33 | }
34 |
35 | @Override
36 | public CompletionStage> load(
37 | final Key key, final Remote remote, final CacheControl control
38 | ) {
39 | return remote.get().handle(
40 | (content, throwable) -> {
41 | final CompletionStage> res;
42 | if (throwable == null && content.isPresent()) {
43 | res = this.storage.save(
44 | key, new Content.From(content.get().size(), content.get())
45 | ).thenCompose(nothing -> this.storage.value(key))
46 | .thenApply(Optional::of);
47 | } else {
48 | final Throwable error;
49 | if (throwable == null) {
50 | error = new ArtipieIOException("Failed to load content from remote");
51 | } else {
52 | error = throwable;
53 | }
54 | res = new FromStorageCache(this.storage)
55 | .load(key, new Remote.Failed(error), control);
56 | }
57 | return res;
58 | }
59 | ).thenCompose(Function.identity());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/ext/PublisherAs.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.ext;
6 |
7 | import com.artipie.asto.Concatenation;
8 | import com.artipie.asto.Content;
9 | import com.artipie.asto.Remaining;
10 | import hu.akarnokd.rxjava2.interop.SingleInterop;
11 | import java.nio.ByteBuffer;
12 | import java.nio.charset.Charset;
13 | import java.nio.charset.StandardCharsets;
14 | import java.util.concurrent.CompletionStage;
15 | import org.reactivestreams.Publisher;
16 |
17 | /**
18 | * Read bytes from content to memory.
19 | * Using this class keep in mind that it reads ByteBuffer from publisher into memory and is not
20 | * suitable for large content.
21 | * @since 0.24
22 | */
23 | public final class PublisherAs {
24 |
25 | /**
26 | * Content to read bytes from.
27 | */
28 | private final Content content;
29 |
30 | /**
31 | * Ctor.
32 | * @param content Content
33 | */
34 | public PublisherAs(final Content content) {
35 | this.content = content;
36 | }
37 |
38 | /**
39 | * Ctor.
40 | * @param content Content
41 | */
42 | public PublisherAs(final Publisher content) {
43 | this(new Content.From(content));
44 | }
45 |
46 | /**
47 | * Reads bytes from content into memory.
48 | * @return Byte array as CompletionStage
49 | */
50 | public CompletionStage bytes() {
51 | return new Concatenation(this.content)
52 | .single()
53 | .map(buf -> new Remaining(buf, true))
54 | .map(Remaining::bytes)
55 | .to(SingleInterop.get());
56 | }
57 |
58 | /**
59 | * Reads bytes from content as string.
60 | * @param charset Charset to read string
61 | * @return String as CompletionStage
62 | */
63 | public CompletionStage string(final Charset charset) {
64 | return this.bytes().thenApply(bytes -> new String(bytes, charset));
65 | }
66 |
67 | /**
68 | * Reads bytes from content as {@link StandardCharsets#US_ASCII} string.
69 | * @return String as CompletionStage
70 | */
71 | public CompletionStage asciiString() {
72 | return this.string(StandardCharsets.US_ASCII);
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/streams/ContentAsStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.streams;
6 |
7 | import com.artipie.asto.ArtipieIOException;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.io.PipedInputStream;
11 | import java.io.PipedOutputStream;
12 | import java.nio.ByteBuffer;
13 | import java.util.concurrent.CompletableFuture;
14 | import java.util.concurrent.CompletionStage;
15 | import java.util.function.Function;
16 | import org.cqfn.rio.WriteGreed;
17 | import org.cqfn.rio.stream.ReactiveOutputStream;
18 | import org.reactivestreams.Publisher;
19 |
20 | /**
21 | * Process content as input stream.
22 | * This class allows to treat storage item as input stream and
23 | * perform some action with this stream (read/uncompress/parse etc).
24 | * @param Result type
25 | * @since 1.4
26 | */
27 | public final class ContentAsStream {
28 |
29 | /**
30 | * Publisher to process.
31 | */
32 | private final Publisher content;
33 |
34 | /**
35 | * Ctor.
36 | * @param content Content
37 | */
38 | public ContentAsStream(final Publisher content) {
39 | this.content = content;
40 | }
41 |
42 | /**
43 | * Process storage item as input stream by performing provided action on it.
44 | * @param action Action to perform
45 | * @return Completion action with the result
46 | */
47 | public CompletionStage process(final Function action) {
48 | return CompletableFuture.supplyAsync(
49 | () -> {
50 | try (
51 | PipedInputStream in = new PipedInputStream();
52 | PipedOutputStream out = new PipedOutputStream(in)
53 | ) {
54 | final CompletionStage ros =
55 | new ReactiveOutputStream(out).write(this.content, WriteGreed.SYSTEM);
56 | final T result = action.apply(in);
57 | return ros.thenApply(nothing -> result);
58 | } catch (final IOException err) {
59 | throw new ArtipieIOException(err);
60 | }
61 | }
62 | ).thenCompose(Function.identity());
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/ArtipieIOException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import com.artipie.ArtipieException;
8 | import java.io.IOException;
9 | import java.io.UncheckedIOException;
10 |
11 | /**
12 | * Artipie input-output exception.
13 | * @since 1.0
14 | * @checkstyle AbbreviationAsWordInNameCheck (10 lines)
15 | */
16 | public class ArtipieIOException extends ArtipieException {
17 |
18 | private static final long serialVersionUID = 862160427262047490L;
19 |
20 | /**
21 | * New IO excption.
22 | * @param cause IO exception
23 | */
24 | public ArtipieIOException(final IOException cause) {
25 | super(cause);
26 | }
27 |
28 | /**
29 | * New IO excption with message.
30 | * @param msg Message
31 | * @param cause IO exception
32 | */
33 | public ArtipieIOException(final String msg, final IOException cause) {
34 | super(msg, cause);
35 | }
36 |
37 | /**
38 | * New IO exception.
39 | * @param cause Unkown exception
40 | */
41 | public ArtipieIOException(final Throwable cause) {
42 | this(ArtipieIOException.unwrap(cause));
43 | }
44 |
45 | /**
46 | * New IO exception.
47 | * @param msg Exception message
48 | * @param cause Unkown exception
49 | */
50 | public ArtipieIOException(final String msg, final Throwable cause) {
51 | this(msg, ArtipieIOException.unwrap(cause));
52 | }
53 |
54 | /**
55 | * New IO exception with message.
56 | * @param msg Exception message
57 | */
58 | public ArtipieIOException(final String msg) {
59 | this(new IOException(msg));
60 | }
61 |
62 | /**
63 | * Resolve unkown exception to IO exception.
64 | * @param cause Unkown exception
65 | * @return IO exception
66 | */
67 | private static IOException unwrap(final Throwable cause) {
68 | final IOException iex;
69 | if (cause instanceof UncheckedIOException) {
70 | iex = ((UncheckedIOException) cause).getCause();
71 | } else if (cause instanceof IOException) {
72 | iex = (IOException) cause;
73 | } else {
74 | iex = new IOException(cause);
75 | }
76 | return iex;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/asto-s3/src/main/java/com/artipie/asto/s3/InternalExceptionHandle.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.s3;
6 |
7 | import com.artipie.asto.ArtipieIOException;
8 | import com.artipie.asto.FailedCompletionStage;
9 | import java.util.concurrent.CompletableFuture;
10 | import java.util.concurrent.CompletionException;
11 | import java.util.concurrent.CompletionStage;
12 | import java.util.function.BiFunction;
13 | import java.util.function.Function;
14 |
15 | /**
16 | * Translate an exception happened inside future.
17 | *
18 | * @param Future result type.
19 | * @since 0.1
20 | */
21 | final class InternalExceptionHandle implements BiFunction> {
22 |
23 | /**
24 | * Type of exception to handle.
25 | */
26 | private final Class extends Throwable> from;
27 |
28 | /**
29 | * Converter to a new exception.
30 | */
31 | private final Function super Throwable, ? extends Throwable> convert;
32 |
33 | /**
34 | * Ctor.
35 | *
36 | * @param from Internal type of exception.
37 | * @param convert Converter to a external type.
38 | */
39 | InternalExceptionHandle(
40 | final Class extends Throwable> from,
41 | final Function super Throwable, ? extends Throwable> convert
42 | ) {
43 | this.from = from;
44 | this.convert = convert;
45 | }
46 |
47 | @Override
48 | public CompletionStage apply(final T content, final Throwable throwable) {
49 | final CompletionStage result;
50 | if (throwable == null) {
51 | result = CompletableFuture.completedFuture(content);
52 | } else {
53 | if (
54 | throwable instanceof CompletionException
55 | && this.from.isInstance(throwable.getCause())
56 | ) {
57 | result = new FailedCompletionStage<>(
58 | this.convert.apply(throwable.getCause())
59 | );
60 | } else if (throwable instanceof CompletionException) {
61 | result = new FailedCompletionStage<>(new ArtipieIOException(throwable.getCause()));
62 | } else {
63 | result = new FailedCompletionStage<>(new ArtipieIOException(throwable));
64 | }
65 | }
66 | return result;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/SplittingTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import io.reactivex.Flowable;
8 | import java.nio.ByteBuffer;
9 | import java.util.List;
10 | import java.util.Random;
11 | import org.hamcrest.MatcherAssert;
12 | import org.hamcrest.Matchers;
13 | import org.junit.jupiter.api.Test;
14 |
15 | /**
16 | * Tests for {@link Splitting}.
17 | *
18 | * @since 1.12.0
19 | * @checkstyle MagicNumberCheck (500 lines)
20 | */
21 | public class SplittingTest {
22 |
23 | @Test
24 | void shouldReturnOneByteBufferWhenOriginalLessSize() {
25 | final byte[] data = new byte[12];
26 | new Random().nextBytes(data);
27 | final List buffers = Flowable.fromPublisher(
28 | new Splitting(ByteBuffer.wrap(data), 24).publisher()
29 | ).toList().blockingGet();
30 | MatcherAssert.assertThat(buffers.size(), Matchers.equalTo(1));
31 | MatcherAssert.assertThat(
32 | new Remaining(buffers.get(0)).bytes(), Matchers.equalTo(data)
33 | );
34 | }
35 |
36 | @Test
37 | void shouldReturnOneByteBufferWhenOriginalEqualsSize() {
38 | final byte[] data = new byte[24];
39 | new Random().nextBytes(data);
40 | final List buffers = Flowable.fromPublisher(
41 | new Splitting(ByteBuffer.wrap(data), 24).publisher()
42 | ).toList().blockingGet();
43 | MatcherAssert.assertThat(buffers.size(), Matchers.equalTo(1));
44 | MatcherAssert.assertThat(
45 | new Remaining(buffers.get(0)).bytes(), Matchers.equalTo(data)
46 | );
47 | }
48 |
49 | @Test
50 | void shouldReturnSeveralByteBuffersWhenOriginalMoreSize() {
51 | final byte[] data = new byte[2 * 24 + 8];
52 | new Random().nextBytes(data);
53 | final List buffers = Flowable.fromPublisher(
54 | new Splitting(ByteBuffer.wrap(data), 24).publisher()
55 | ).toList().blockingGet();
56 | MatcherAssert.assertThat(buffers.size(), Matchers.equalTo(3));
57 | MatcherAssert.assertThat(
58 | new Remaining(
59 | new Concatenation(
60 | Flowable.fromIterable(buffers)
61 | ).single().blockingGet()
62 | ).bytes(), Matchers.equalTo(data)
63 | );
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/memory/BenchmarkStorageDeleteTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.memory;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.Key;
9 | import com.artipie.asto.ValueNotFoundException;
10 | import com.artipie.asto.ext.PublisherAs;
11 | import java.util.NavigableMap;
12 | import java.util.TreeMap;
13 | import java.util.concurrent.CompletionException;
14 | import org.hamcrest.MatcherAssert;
15 | import org.hamcrest.core.IsEqual;
16 | import org.hamcrest.core.IsInstanceOf;
17 | import org.junit.jupiter.api.Assertions;
18 | import org.junit.jupiter.api.Test;
19 |
20 | /**
21 | * Tests for {@link BenchmarkStorage#delete(Key)}.
22 | * @since 1.2.0
23 | */
24 | @SuppressWarnings("PMD.AvoidDuplicateLiterals")
25 | final class BenchmarkStorageDeleteTest {
26 | @Test
27 | void obtainsValueWhichWasAddedBySameKeyAfterDeletionToVerifyDeletedWasReset() {
28 | final InMemoryStorage memory = new InMemoryStorage();
29 | final BenchmarkStorage bench = new BenchmarkStorage(memory);
30 | final Key key = new Key.From("somekey");
31 | bench.save(key, new Content.From("old data".getBytes())).join();
32 | bench.delete(key).join();
33 | final byte[] upd = "updated data".getBytes();
34 | bench.save(key, new Content.From(upd)).join();
35 | MatcherAssert.assertThat(
36 | new PublisherAs(bench.value(key).join())
37 | .bytes()
38 | .toCompletableFuture().join(),
39 | new IsEqual<>(upd)
40 | );
41 | }
42 |
43 | @Test
44 | void returnsNotFoundIfValueWasDeletedButPresentInBackend() {
45 | final Key key = new Key.From("somekey");
46 | final NavigableMap backdata = new TreeMap<>();
47 | backdata.put(key.string(), "shouldBeObtained".getBytes());
48 | final InMemoryStorage memory = new InMemoryStorage(backdata);
49 | final BenchmarkStorage bench = new BenchmarkStorage(memory);
50 | bench.delete(key).join();
51 | final Throwable thr = Assertions.assertThrows(
52 | CompletionException.class,
53 | () -> bench.value(key).join()
54 | );
55 | MatcherAssert.assertThat(
56 | thr.getCause(),
57 | new IsInstanceOf(ValueNotFoundException.class)
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/asto-redis/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 | asto
28 | com.artipie
29 | 1.0-SNAPSHOT
30 |
31 | 4.0.0
32 | asto-redis
33 |
34 |
35 | com.artipie
36 | asto-core
37 | 1.0-SNAPSHOT
38 | compile
39 |
40 |
41 | org.redisson
42 | redisson
43 | 3.17.4
44 |
45 |
46 |
47 |
48 |
49 |
50 | maven-surefire-plugin
51 |
52 | false
53 | false
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/asto-s3/src/test/java/com/artipie/asto/S3StorageWhiteboxVerificationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import com.adobe.testing.s3mock.junit5.S3MockExtension;
8 | import com.artipie.asto.s3.S3Storage;
9 | import com.artipie.asto.test.StorageWhiteboxVerification;
10 | import java.net.URI;
11 | import java.util.UUID;
12 | import org.junit.jupiter.api.AfterAll;
13 | import org.junit.jupiter.api.BeforeAll;
14 | import org.junit.jupiter.api.condition.DisabledOnOs;
15 | import org.junit.jupiter.api.condition.OS;
16 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
17 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
18 | import software.amazon.awssdk.regions.Region;
19 | import software.amazon.awssdk.services.s3.S3AsyncClient;
20 | import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
21 |
22 | /**
23 | * S3 storage verification test.
24 | *
25 | * @checkstyle ProtectedMethodInFinalClassCheck (500 lines)
26 | * @since 0.1
27 | */
28 | @SuppressWarnings("PMD.TestClassWithoutTestCases")
29 | @DisabledOnOs(OS.WINDOWS)
30 | public final class S3StorageWhiteboxVerificationTest extends StorageWhiteboxVerification {
31 |
32 | /**
33 | * S3 mock server extension.
34 | */
35 | private static final S3MockExtension MOCK = S3MockExtension.builder()
36 | .withSecureConnection(false).build();
37 |
38 | @Override
39 | protected Storage newStorage() {
40 | final String endpoint = String.format("http://localhost:%d", MOCK.getHttpPort());
41 | final S3AsyncClient client = S3AsyncClient.builder()
42 | .region(Region.of("us-east-1"))
43 | .credentialsProvider(
44 | StaticCredentialsProvider.create(
45 | AwsBasicCredentials.create("foo", "bar")
46 | )
47 | )
48 | .endpointOverride(URI.create(endpoint))
49 | .build();
50 | final String bucket = UUID.randomUUID().toString();
51 | client.createBucket(CreateBucketRequest.builder().bucket(bucket).build()).join();
52 | return new S3Storage(client, bucket, endpoint);
53 | }
54 |
55 | @BeforeAll
56 | static void setUp() throws Exception {
57 | S3StorageWhiteboxVerificationTest.MOCK.beforeAll(null);
58 | }
59 |
60 | @AfterAll
61 | static void tearDown() {
62 | S3StorageWhiteboxVerificationTest.MOCK.afterAll(null);
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/UnderLockOperation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import com.artipie.asto.lock.Lock;
8 | import java.util.concurrent.CompletableFuture;
9 | import java.util.concurrent.CompletionStage;
10 | import java.util.function.Function;
11 |
12 | /**
13 | * Operation performed under lock.
14 | *
15 | * @param Operation result type.
16 | * @since 0.27
17 | */
18 | public final class UnderLockOperation {
19 |
20 | /**
21 | * Lock.
22 | */
23 | private final Lock lock;
24 |
25 | /**
26 | * Operation.
27 | */
28 | private final Function> operation;
29 |
30 | /**
31 | * Ctor.
32 | *
33 | * @param lock Lock.
34 | * @param operation Operation.
35 | */
36 | public UnderLockOperation(
37 | final Lock lock,
38 | final Function> operation
39 | ) {
40 | this.lock = lock;
41 | this.operation = operation;
42 | }
43 |
44 | /**
45 | * Perform operation under lock on storage.
46 | *
47 | * @param storage Storage.
48 | * @return Operation result.
49 | * @checkstyle IllegalCatchCheck (10 lines)
50 | */
51 | @SuppressWarnings("PMD.AvoidCatchingThrowable")
52 | public CompletionStage perform(final Storage storage) {
53 | return this.lock.acquire().thenCompose(
54 | nothing -> {
55 | CompletionStage result;
56 | try {
57 | result = this.operation.apply(storage);
58 | } catch (final Throwable throwable) {
59 | result = new FailedCompletionStage<>(throwable);
60 | }
61 | return result.handle(
62 | (value, throwable) -> this.lock.release().thenCompose(
63 | released -> {
64 | final CompletableFuture future = new CompletableFuture<>();
65 | if (throwable == null) {
66 | future.complete(value);
67 | } else {
68 | future.completeExceptionally(throwable);
69 | }
70 | return future;
71 | }
72 | )
73 | ).thenCompose(Function.identity());
74 | }
75 | );
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/ByteArray.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * Byte array wrapper with ability to transform it to
11 | * boxed and primitive array.
12 | *
13 | * @since 0.7
14 | */
15 | public final class ByteArray {
16 |
17 | /**
18 | * Bytes.
19 | */
20 | private final Byte[] bytes;
21 |
22 | /**
23 | * Ctor for a list of byes.
24 | *
25 | * @param bytes The list of bytes
26 | */
27 | public ByteArray(final List bytes) {
28 | this(fromList(bytes));
29 | }
30 |
31 | /**
32 | * Ctor for a primitive array.
33 | *
34 | * @param bytes The primitive bytes
35 | */
36 | public ByteArray(final byte[] bytes) {
37 | this(boxed(bytes));
38 | }
39 |
40 | /**
41 | * Ctor.
42 | *
43 | * @param bytes The bytes.
44 | */
45 | @SuppressWarnings("PMD.ArrayIsStoredDirectly")
46 | public ByteArray(final Byte[] bytes) {
47 | this.bytes = bytes;
48 | }
49 |
50 | /**
51 | * Return primitive byte array.
52 | *
53 | * @return Primitive byte array
54 | */
55 | public byte[] primitiveBytes() {
56 | final byte[] result = new byte[this.bytes.length];
57 | for (int itr = 0; itr < this.bytes.length; itr += 1) {
58 | result[itr] = this.bytes[itr];
59 | }
60 | return result;
61 | }
62 |
63 | /**
64 | * Return primitive byte array.
65 | *
66 | * @return Primitive byte array
67 | */
68 | @SuppressWarnings("PMD.MethodReturnsInternalArray")
69 | public Byte[] boxedBytes() {
70 | return this.bytes;
71 | }
72 |
73 | /**
74 | * Convert primitive to boxed array.
75 | * @param primitive Primitive byte array
76 | * @return Boxed byte array
77 | */
78 | @SuppressWarnings("PMD.AvoidArrayLoops")
79 | private static Byte[] boxed(final byte[] primitive) {
80 | final Byte[] res = new Byte[primitive.length];
81 | for (int itr = 0; itr < primitive.length; itr += 1) {
82 | res[itr] = primitive[itr];
83 | }
84 | return res;
85 | }
86 |
87 | /**
88 | * Convert list of bytes to byte array.
89 | * @param list The list of bytes.
90 | * @return Boxed byte array
91 | */
92 | private static Byte[] fromList(final List list) {
93 | return list.toArray(new Byte[0]);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/factory/StoragesLoader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.factory;
6 |
7 | import com.artipie.ArtipieException;
8 | import com.artipie.asto.Storage;
9 | import java.util.Arrays;
10 | import java.util.Collections;
11 | import java.util.Map;
12 | import java.util.Set;
13 |
14 | /**
15 | * Storages to get instance of storage.
16 | *
17 | * @since 1.13.0
18 | */
19 | public final class StoragesLoader
20 | extends FactoryLoader {
21 |
22 | /**
23 | * Environment parameter to define packages to find storage factories.
24 | * Package names should be separated by semicolon ';'.
25 | */
26 | public static final String SCAN_PACK = "STORAGE_FACTORY_SCAN_PACKAGES";
27 |
28 | /**
29 | * Ctor.
30 | */
31 | public StoragesLoader() {
32 | this(System.getenv());
33 | }
34 |
35 | /**
36 | * Ctor.
37 | *
38 | * @param env Environment parameters.
39 | */
40 | public StoragesLoader(final Map env) {
41 | super(ArtipieStorageFactory.class, env);
42 | }
43 |
44 | @Override
45 | public Storage newObject(final String type, final Config cfg) {
46 | final StorageFactory factory = super.factories.get(type);
47 | if (factory == null) {
48 | throw new StorageNotFoundException(type);
49 | }
50 | return factory.newStorage(cfg);
51 | }
52 |
53 | /**
54 | * Known storage types.
55 | *
56 | * @return Set of storage types.
57 | */
58 | public Set types() {
59 | return this.factories.keySet();
60 | }
61 |
62 | @Override
63 | public Set defPackages() {
64 | return Collections.singleton("com.artipie.asto");
65 | }
66 |
67 | @Override
68 | public String scanPackagesEnv() {
69 | return StoragesLoader.SCAN_PACK;
70 | }
71 |
72 | @Override
73 | public String getFactoryName(final Class> element) {
74 | return Arrays.stream(element.getAnnotations())
75 | .filter(ArtipieStorageFactory.class::isInstance)
76 | .map(a -> ((ArtipieStorageFactory) a).value())
77 | .findFirst()
78 | .orElseThrow(
79 | // @checkstyle LineLengthCheck (3 lines)
80 | () -> new ArtipieException("Annotation 'ArtipieStorageFactory' should have a not empty value")
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/rx/RxStorage.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.rx;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.Key;
9 | import io.reactivex.Completable;
10 | import io.reactivex.Single;
11 | import java.util.Collection;
12 | import java.util.function.Function;
13 |
14 | /**
15 | * A reactive version of {@link com.artipie.asto.Storage}.
16 | *
17 | * @since 0.10
18 | */
19 | public interface RxStorage {
20 |
21 | /**
22 | * This file exists?
23 | *
24 | * @param key The key (file name)
25 | * @return TRUE if exists, FALSE otherwise
26 | */
27 | Single exists(Key key);
28 |
29 | /**
30 | * Return the list of keys that start with this prefix, for
31 | * example "foo/bar/".
32 | *
33 | * @param prefix The prefix.
34 | * @return Collection of relative keys.
35 | */
36 | Single> list(Key prefix);
37 |
38 | /**
39 | * Saves the bytes to the specified key.
40 | *
41 | * @param key The key
42 | * @param content Bytes to save
43 | * @return Completion or error signal.
44 | */
45 | Completable save(Key key, Content content);
46 |
47 | /**
48 | * Moves value from one location to another.
49 | *
50 | * @param source Source key.
51 | * @param destination Destination key.
52 | * @return Completion or error signal.
53 | */
54 | Completable move(Key source, Key destination);
55 |
56 | /**
57 | * Get value size.
58 | *
59 | * @param key The key of value.
60 | * @return Size of value in bytes.
61 | */
62 | Single size(Key key);
63 |
64 | /**
65 | * Obtain bytes by key.
66 | *
67 | * @param key The key
68 | * @return Bytes.
69 | */
70 | Single value(Key key);
71 |
72 | /**
73 | * Removes value from storage. Fails if value does not exist.
74 | *
75 | * @param key Key for value to be deleted.
76 | * @return Completion or error signal.
77 | */
78 | Completable delete(Key key);
79 |
80 | /**
81 | * Runs operation exclusively for specified key.
82 | *
83 | * @param key Key which is scope of operation.
84 | * @param operation Operation to be performed exclusively.
85 | * @param Operation result type.
86 | * @return Result of operation.
87 | */
88 | Single exclusively(
89 | Key key,
90 | Function> operation
91 | );
92 | }
93 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/memory/BenchmarkStorageSizeTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.memory;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.Key;
9 | import com.artipie.asto.ValueNotFoundException;
10 | import java.util.NavigableMap;
11 | import java.util.TreeMap;
12 | import java.util.concurrent.CompletionException;
13 | import org.hamcrest.MatcherAssert;
14 | import org.hamcrest.core.IsEqual;
15 | import org.hamcrest.core.IsInstanceOf;
16 | import org.junit.jupiter.api.Assertions;
17 | import org.junit.jupiter.api.Test;
18 |
19 | /**
20 | * Tests for {@link BenchmarkStorage#size(Key)}.
21 | * @since 1.2.0
22 | */
23 | @SuppressWarnings("deprecation")
24 | final class BenchmarkStorageSizeTest {
25 | @Test
26 | void returnsSizeWhenPresentInLocalAndNotDeleted() {
27 | final byte[] data = "example data".getBytes();
28 | final InMemoryStorage memory = new InMemoryStorage();
29 | final BenchmarkStorage bench = new BenchmarkStorage(memory);
30 | final Key key = new Key.From("someLocalKey");
31 | bench.save(key, new Content.From(data)).join();
32 | MatcherAssert.assertThat(
33 | bench.size(key).join(),
34 | new IsEqual<>((long) data.length)
35 | );
36 | }
37 |
38 | @Test
39 | void returnsSizeWhenPresentInBackendAndNotDeleted() {
40 | final byte[] data = "super data".getBytes();
41 | final Key key = new Key.From("someBackendKey");
42 | final NavigableMap backdata = new TreeMap<>();
43 | backdata.put(key.string(), data);
44 | final InMemoryStorage memory = new InMemoryStorage(backdata);
45 | final BenchmarkStorage bench = new BenchmarkStorage(memory);
46 | MatcherAssert.assertThat(
47 | bench.size(key).join(),
48 | new IsEqual<>((long) data.length)
49 | );
50 | }
51 |
52 | @Test
53 | void throwsIfKeyWasDeleted() {
54 | final InMemoryStorage memory = new InMemoryStorage();
55 | final BenchmarkStorage bench = new BenchmarkStorage(memory);
56 | final Key key = new Key.From("somekey");
57 | bench.save(key, new Content.From("will be deleted".getBytes())).join();
58 | bench.delete(key).join();
59 | final Throwable thr = Assertions.assertThrows(
60 | CompletionException.class,
61 | () -> bench.size(key).join()
62 | );
63 | MatcherAssert.assertThat(
64 | thr.getCause(),
65 | new IsInstanceOf(ValueNotFoundException.class)
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/lock/RetryLock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.lock;
6 |
7 | import io.github.resilience4j.core.IntervalFunction;
8 | import io.github.resilience4j.retry.RetryConfig;
9 | import io.github.resilience4j.retry.internal.InMemoryRetryRegistry;
10 | import java.util.concurrent.CompletionStage;
11 | import java.util.concurrent.ScheduledExecutorService;
12 |
13 | /**
14 | * Lock that tries to obtain origin {@link Lock} with retries.
15 | *
16 | * @since 0.24
17 | */
18 | public final class RetryLock implements Lock {
19 |
20 | /**
21 | * Max number of attempts by default.
22 | */
23 | private static final int MAX_ATTEMPTS = 3;
24 |
25 | /**
26 | * Scheduler to use for retry triggering.
27 | */
28 | private final ScheduledExecutorService scheduler;
29 |
30 | /**
31 | * Origin lock.
32 | */
33 | private final Lock origin;
34 |
35 | /**
36 | * Retry registry to store retries state.
37 | */
38 | private final InMemoryRetryRegistry registry;
39 |
40 | /**
41 | * Ctor.
42 | *
43 | * @param scheduler Scheduler to use for retry triggering.
44 | * @param origin Origin lock.
45 | */
46 | public RetryLock(final ScheduledExecutorService scheduler, final Lock origin) {
47 | this(
48 | scheduler,
49 | origin,
50 | new RetryConfig.Builder<>()
51 | .maxAttempts(RetryLock.MAX_ATTEMPTS)
52 | .intervalFunction(IntervalFunction.ofExponentialBackoff())
53 | .build()
54 | );
55 | }
56 |
57 | /**
58 | * Ctor.
59 | *
60 | * @param scheduler Scheduler to use for retry triggering.
61 | * @param origin Origin lock.
62 | * @param config Retry strategy.
63 | */
64 | public RetryLock(
65 | final ScheduledExecutorService scheduler,
66 | final Lock origin,
67 | final RetryConfig config
68 | ) {
69 | this.scheduler = scheduler;
70 | this.origin = origin;
71 | this.registry = new InMemoryRetryRegistry(config);
72 | }
73 |
74 | @Override
75 | public CompletionStage acquire() {
76 | return this.registry.retry("lock-acquire").executeCompletionStage(
77 | this.scheduler,
78 | this.origin::acquire
79 | );
80 | }
81 |
82 | @Override
83 | public CompletionStage release() {
84 | return this.registry.retry("lock-release").executeCompletionStage(
85 | this.scheduler,
86 | this.origin::release
87 | );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/asto-s3/src/test/java/com/artipie/asto/S3StorageFactoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import com.amihaiemil.eoyaml.Yaml;
8 | import com.artipie.asto.factory.Config;
9 | import com.artipie.asto.factory.StoragesLoader;
10 | import com.artipie.asto.s3.S3Storage;
11 | import org.hamcrest.MatcherAssert;
12 | import org.hamcrest.core.IsInstanceOf;
13 | import org.junit.jupiter.api.Test;
14 |
15 | /**
16 | * Test for Storages.
17 | *
18 | * @since 0.1
19 | */
20 | @SuppressWarnings("PMD.AvoidDuplicateLiterals")
21 | public final class S3StorageFactoryTest {
22 |
23 | /**
24 | * Test for S3 storage factory.
25 | *
26 | * @checkstyle MethodNameCheck (3 lines)
27 | */
28 | @Test
29 | void shouldCreateS3StorageConfigHasCredentials() {
30 | MatcherAssert.assertThat(
31 | new StoragesLoader()
32 | .newObject(
33 | "s3",
34 | new Config.YamlStorageConfig(
35 | Yaml.createYamlMappingBuilder()
36 | .add("region", "us-east-1")
37 | .add("bucket", "aaa")
38 | .add("endpoint", "http://localhost")
39 | .add(
40 | "credentials",
41 | Yaml.createYamlMappingBuilder()
42 | .add("type", "basic")
43 | .add("accessKeyId", "foo")
44 | .add("secretAccessKey", "bar")
45 | .build()
46 | )
47 | .build()
48 | )
49 | ),
50 | new IsInstanceOf(S3Storage.class)
51 | );
52 | }
53 |
54 | /**
55 | * Test for S3 storage factory.
56 | *
57 | * @checkstyle MethodNameCheck (3 lines)
58 | */
59 | @Test
60 | void shouldCreateS3StorageConfigDoesNotHaveCredentials() {
61 | MatcherAssert.assertThat(
62 | new StoragesLoader()
63 | .newObject(
64 | "s3",
65 | new Config.YamlStorageConfig(
66 | Yaml.createYamlMappingBuilder()
67 | .add("region", "us-east-1")
68 | .add("bucket", "aaa")
69 | .add("endpoint", "http://localhost")
70 | .build()
71 | )
72 | ),
73 | new IsInstanceOf(S3Storage.class)
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/Copy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import com.artipie.asto.rx.RxStorageWrapper;
8 | import hu.akarnokd.rxjava2.interop.CompletableInterop;
9 | import io.reactivex.Observable;
10 | import java.util.Collection;
11 | import java.util.HashSet;
12 | import java.util.Set;
13 | import java.util.concurrent.CompletableFuture;
14 | import java.util.function.Predicate;
15 | import java.util.stream.Collectors;
16 |
17 | /**
18 | * Storage synchronization.
19 | * @since 0.19
20 | */
21 | public class Copy {
22 |
23 | /**
24 | * The storage to copy from.
25 | */
26 | private final Storage from;
27 |
28 | /**
29 | * Predicate condition to copy keys.
30 | */
31 | private final Predicate super Key> predicate;
32 |
33 | /**
34 | * Ctor.
35 | *
36 | * @param from The storage to copy to.
37 | */
38 | public Copy(final Storage from) {
39 | this(from, item -> true);
40 | }
41 |
42 | /**
43 | * Ctor.
44 | * @param from The storage to copy to
45 | * @param keys The keys to copy
46 | */
47 | public Copy(final Storage from, final Collection keys) {
48 | this(from, new HashSet<>(keys));
49 | }
50 |
51 | /**
52 | * Ctor.
53 | * @param from The storage to copy to
54 | * @param keys The keys to copy
55 | */
56 | public Copy(final Storage from, final Set keys) {
57 | this(from, keys::contains);
58 | }
59 |
60 | /**
61 | * Ctor.
62 | *
63 | * @param from The storage to copy to
64 | * @param predicate Predicate to copy items
65 | */
66 | public Copy(final Storage from, final Predicate super Key> predicate) {
67 | this.from = from;
68 | this.predicate = predicate;
69 | }
70 |
71 | /**
72 | * Copy keys to the specified storage.
73 | * @param dest Destination storage
74 | * @return When copy operation completes
75 | */
76 | public CompletableFuture copy(final Storage dest) {
77 | final RxStorageWrapper rxdst = new RxStorageWrapper(dest);
78 | final RxStorageWrapper rxsrc = new RxStorageWrapper(this.from);
79 | return rxsrc.list(Key.ROOT)
80 | .map(lst -> lst.stream().filter(this.predicate).collect(Collectors.toList()))
81 | .flatMapObservable(Observable::fromIterable)
82 | .flatMapCompletable(
83 | key -> rxsrc.value(key).flatMapCompletable(content -> rxdst.save(key, content))
84 | )
85 | .to(CompletableInterop.await())
86 | .thenApply(ignore -> (Void) null)
87 | .toCompletableFuture();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/cache/FromStorageCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.cache;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.Key;
9 | import com.artipie.asto.Storage;
10 | import com.artipie.asto.rx.RxStorageWrapper;
11 | import com.jcabi.log.Logger;
12 | import hu.akarnokd.rxjava2.interop.SingleInterop;
13 | import io.reactivex.Single;
14 | import java.util.Optional;
15 | import java.util.concurrent.CompletionStage;
16 |
17 | /**
18 | * Cache implementation that tries to obtain items from storage cache,
19 | * validates it and returns if valid. If item is not present in storage or is not valid,
20 | * it is loaded from remote.
21 | * @since 0.24
22 | */
23 | public final class FromStorageCache implements Cache {
24 |
25 | /**
26 | * Back-end storage.
27 | */
28 | private final Storage storage;
29 |
30 | /**
31 | * New storage cache.
32 | * @param storage Back-end storage for cache
33 | */
34 | public FromStorageCache(final Storage storage) {
35 | this.storage = storage;
36 | }
37 |
38 | @Override
39 | public CompletionStage> load(final Key key, final Remote remote,
40 | final CacheControl control) {
41 | final RxStorageWrapper rxsto = new RxStorageWrapper(this.storage);
42 | return rxsto.exists(key)
43 | .filter(exists -> exists)
44 | .flatMapSingleElement(
45 | exists -> SingleInterop.fromFuture(
46 | control.validate(key, () -> this.storage.value(key).thenApply(Optional::of))
47 | )
48 | )
49 | .filter(valid -> valid)
50 | .>flatMapSingleElement(
51 | ignore -> rxsto.value(key).map(Optional::of)
52 | )
53 | .doOnError(err -> Logger.warn(this, "Failed to read cached item: %[exception]s", err))
54 | .onErrorComplete()
55 | .switchIfEmpty(
56 | SingleInterop.fromFuture(remote.get()).flatMap(
57 | content -> {
58 | final Single> res;
59 | if (content.isPresent()) {
60 | res = rxsto.save(
61 | key, new Content.From(content.get().size(), content.get())
62 | ).andThen(rxsto.value(key)).map(Optional::of);
63 | } else {
64 | res = Single.fromCallable(Optional::empty);
65 | }
66 | return res;
67 | }
68 | )
69 | ).to(SingleInterop.get());
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/asto-redis/src/test/java/com/artipie/asto/redis/RedisStorageWhiteboxVerificationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.redis;
6 |
7 | import com.amihaiemil.eoyaml.Yaml;
8 | import com.artipie.asto.Storage;
9 | import com.artipie.asto.factory.Config;
10 | import com.artipie.asto.factory.StoragesLoader;
11 | import com.artipie.asto.test.StorageWhiteboxVerification;
12 | import org.junit.jupiter.api.AfterAll;
13 | import org.junit.jupiter.api.BeforeAll;
14 | import org.junit.jupiter.api.condition.DisabledOnOs;
15 | import org.junit.jupiter.api.condition.OS;
16 | import org.testcontainers.containers.GenericContainer;
17 |
18 | /**
19 | * Redis storage verification test.
20 | *
21 | * @checkstyle ProtectedMethodInFinalClassCheck (500 lines)
22 | * @since 0.1
23 | */
24 | @SuppressWarnings({"PMD.TestClassWithoutTestCases", "PMD.AvoidDuplicateLiterals"})
25 | @DisabledOnOs(OS.WINDOWS)
26 | public final class RedisStorageWhiteboxVerificationTest extends StorageWhiteboxVerification {
27 |
28 | /**
29 | * Default redis port.
30 | */
31 | private static final int DEF_PORT = 6379;
32 |
33 | /**
34 | * Redis test container.
35 | */
36 | private static GenericContainer> redis;
37 |
38 | /**
39 | * Redis storage.
40 | */
41 | private static Storage storage;
42 |
43 | @Override
44 | protected Storage newStorage() {
45 | return RedisStorageWhiteboxVerificationTest.storage;
46 | }
47 |
48 | @BeforeAll
49 | static void setUp() {
50 | RedisStorageWhiteboxVerificationTest.redis = new GenericContainer<>("redis:3-alpine")
51 | .withExposedPorts(RedisStorageWhiteboxVerificationTest.DEF_PORT);
52 | RedisStorageWhiteboxVerificationTest.redis.start();
53 | RedisStorageWhiteboxVerificationTest.storage = new StoragesLoader().newObject(
54 | "redis", config(RedisStorageWhiteboxVerificationTest.redis.getFirstMappedPort())
55 | );
56 | }
57 |
58 | @AfterAll
59 | static void tearDown() {
60 | RedisStorageWhiteboxVerificationTest.redis.stop();
61 | }
62 |
63 | private static Config config(final Integer port) {
64 | return new Config.YamlStorageConfig(
65 | Yaml.createYamlMappingBuilder()
66 | .add("type", "redis")
67 | .add(
68 | "config",
69 | Yaml.createYamlMappingBuilder()
70 | .add(
71 | "singleServerConfig",
72 | Yaml.createYamlMappingBuilder()
73 | .add(
74 | "address",
75 | String.format("redis://127.0.0.1:%d", port)
76 | ).build()
77 | ).build()
78 | ).build()
79 | );
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/cache/Remote.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.cache;
6 |
7 | import com.artipie.asto.Content;
8 | import com.jcabi.log.Logger;
9 | import java.util.Optional;
10 | import java.util.concurrent.CompletableFuture;
11 | import java.util.concurrent.CompletionStage;
12 | import java.util.function.Supplier;
13 |
14 | /**
15 | * Async {@link java.util.function.Supplier} of {@link java.util.concurrent.CompletionStage}
16 | * with {@link Optional} of {@link Content}. It's a {@link FunctionalInterface}.
17 | *
18 | * @since 0.32
19 | */
20 | @FunctionalInterface
21 | public interface Remote extends Supplier>> {
22 |
23 | /**
24 | * Empty remote.
25 | */
26 | Remote EMPTY = () -> CompletableFuture.completedFuture(Optional.empty());
27 |
28 | @Override
29 | CompletionStage> get();
30 |
31 | /**
32 | * Implementation of {@link Remote} that handle all possible errors and returns
33 | * empty {@link Optional} if any exception happened.
34 | * @since 0.32
35 | */
36 | class WithErrorHandling implements Remote {
37 |
38 | /**
39 | * Origin.
40 | */
41 | private final Remote origin;
42 |
43 | /**
44 | * Ctor.
45 | * @param origin Origin
46 | */
47 | public WithErrorHandling(final Remote origin) {
48 | this.origin = origin;
49 | }
50 |
51 | @Override
52 | public CompletionStage> get() {
53 | return this.origin.get().handle(
54 | (content, throwable) -> {
55 | final Optional extends Content> res;
56 | if (throwable == null) {
57 | res = content;
58 | } else {
59 | Logger.error(this.origin.getClass(), throwable.getMessage());
60 | res = Optional.empty();
61 | }
62 | return res;
63 | }
64 | );
65 | }
66 | }
67 |
68 | /**
69 | * Failed remote.
70 | * @since 0.32
71 | */
72 | final class Failed implements Remote {
73 |
74 | /**
75 | * Failure cause.
76 | */
77 | private final Throwable reason;
78 |
79 | /**
80 | * Ctor.
81 | * @param reason Failure cause
82 | */
83 | public Failed(final Throwable reason) {
84 | this.reason = reason;
85 | }
86 |
87 | @Override
88 | public CompletionStage> get() {
89 | final CompletableFuture> res = new CompletableFuture<>();
90 | res.completeExceptionally(this.reason);
91 | return res;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/asto-core/src/main/java/com/artipie/asto/rx/RxStorageWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.rx;
6 |
7 | import com.artipie.asto.Content;
8 | import com.artipie.asto.Key;
9 | import com.artipie.asto.Storage;
10 | import hu.akarnokd.rxjava2.interop.CompletableInterop;
11 | import hu.akarnokd.rxjava2.interop.SingleInterop;
12 | import io.reactivex.Completable;
13 | import io.reactivex.Single;
14 | import java.util.Collection;
15 | import java.util.function.Function;
16 |
17 | /**
18 | * Reactive wrapper over {@code Storage}.
19 | *
20 | * @since 0.9
21 | */
22 | public final class RxStorageWrapper implements RxStorage {
23 |
24 | /**
25 | * Wrapped storage.
26 | */
27 | private final Storage storage;
28 |
29 | /**
30 | * Ctor.
31 | *
32 | * @param storage The storage
33 | */
34 | public RxStorageWrapper(final Storage storage) {
35 | this.storage = storage;
36 | }
37 |
38 | @Override
39 | public Single exists(final Key key) {
40 | return Single.defer(() -> SingleInterop.fromFuture(this.storage.exists(key)));
41 | }
42 |
43 | @Override
44 | public Single> list(final Key prefix) {
45 | return Single.defer(() -> SingleInterop.fromFuture(this.storage.list(prefix)));
46 | }
47 |
48 | @Override
49 | public Completable save(final Key key, final Content content) {
50 | return Completable.defer(
51 | () -> CompletableInterop.fromFuture(this.storage.save(key, content))
52 | );
53 | }
54 |
55 | @Override
56 | public Completable move(final Key source, final Key destination) {
57 | return Completable.defer(
58 | () -> CompletableInterop.fromFuture(this.storage.move(source, destination))
59 | );
60 | }
61 |
62 | // @checkstyle MissingDeprecatedCheck (5 lines)
63 | @Override
64 | @Deprecated
65 | public Single size(final Key key) {
66 | return Single.defer(() -> SingleInterop.fromFuture(this.storage.size(key)));
67 | }
68 |
69 | @Override
70 | public Single value(final Key key) {
71 | return Single.defer(() -> SingleInterop.fromFuture(this.storage.value(key)));
72 | }
73 |
74 | @Override
75 | public Completable delete(final Key key) {
76 | return Completable.defer(() -> CompletableInterop.fromFuture(this.storage.delete(key)));
77 | }
78 |
79 | @Override
80 | public Single exclusively(
81 | final Key key,
82 | final Function> operation
83 | ) {
84 | return Single.defer(
85 | () -> SingleInterop.fromFuture(
86 | this.storage.exclusively(
87 | key,
88 | st -> operation.apply(new RxStorageWrapper(st)).to(SingleInterop.get())
89 | )
90 | )
91 | );
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/asto-core/src/test/java/com/artipie/asto/CopyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto;
6 |
7 | import com.artipie.asto.blocking.BlockingStorage;
8 | import com.artipie.asto.memory.InMemoryStorage;
9 | import java.util.Arrays;
10 | import java.util.concurrent.ExecutionException;
11 | import org.hamcrest.MatcherAssert;
12 | import org.hamcrest.Matchers;
13 | import org.hamcrest.core.IsEqual;
14 | import org.junit.jupiter.api.Test;
15 |
16 | /**
17 | * A test for {@link Copy}.
18 | * @since 0.19
19 | * @checkstyle LocalFinalVariableNameCheck (500 lines)
20 | */
21 | public class CopyTest {
22 |
23 | @Test
24 | public void copyTwoFilesFromOneStorageToAnotherWorksFine()
25 | throws ExecutionException, InterruptedException {
26 | final Storage from = new InMemoryStorage();
27 | final Storage to = new InMemoryStorage();
28 | final Key akey = new Key.From("a.txt");
29 | final Key bkey = new Key.From("b.txt");
30 | final BlockingStorage bfrom = new BlockingStorage(from);
31 | bfrom.save(akey, "Hello world A".getBytes());
32 | bfrom.save(bkey, "Hello world B".getBytes());
33 | new Copy(from, Arrays.asList(akey, bkey)).copy(to).get();
34 | for (final Key key : new BlockingStorage(from).list(Key.ROOT)) {
35 | MatcherAssert.assertThat(
36 | Arrays.equals(
37 | bfrom.value(key),
38 | new BlockingStorage(to).value(key)
39 | ),
40 | Matchers.is(true)
41 | );
42 | }
43 | }
44 |
45 | @Test
46 | public void copyEverythingFromOneStorageToAnotherWorksFine() {
47 | final Storage from = new InMemoryStorage();
48 | final Storage to = new InMemoryStorage();
49 | final Key akey = new Key.From("a/b/c");
50 | final Key bkey = new Key.From("foo.bar");
51 | final BlockingStorage bfrom = new BlockingStorage(from);
52 | bfrom.save(akey, "one".getBytes());
53 | bfrom.save(bkey, "two".getBytes());
54 | new Copy(from).copy(to).join();
55 | for (final Key key : bfrom.list(Key.ROOT)) {
56 | MatcherAssert.assertThat(
57 | new BlockingStorage(to).value(key),
58 | new IsEqual<>(bfrom.value(key))
59 | );
60 | }
61 | }
62 |
63 | @Test
64 | public void copyPredicate() {
65 | final Storage src = new InMemoryStorage();
66 | final Storage dst = new InMemoryStorage();
67 | final Key foo = new Key.From("foo");
68 | new BlockingStorage(src).save(foo, new byte[]{0x00});
69 | new BlockingStorage(src).save(new Key.From("bar/baz"), new byte[]{0x00});
70 | new Copy(src, key -> key.string().contains("oo")).copy(dst).join();
71 | MatcherAssert.assertThat(
72 | new BlockingStorage(dst).list(Key.ROOT),
73 | Matchers.contains(foo)
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/asto-s3/src/test/java/com/artipie/asto/s3/InternalExceptionHandleTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
3 | * https://github.com/artipie/asto/LICENSE.txt
4 | */
5 | package com.artipie.asto.s3;
6 |
7 | import com.artipie.asto.ArtipieIOException;
8 | import java.util.concurrent.CompletableFuture;
9 | import java.util.concurrent.ExecutionException;
10 | import java.util.function.Function;
11 | import org.hamcrest.MatcherAssert;
12 | import org.hamcrest.Matchers;
13 | import org.junit.jupiter.api.Assertions;
14 | import org.junit.jupiter.api.Test;
15 |
16 | /**
17 | * Test for {@link InternalExceptionHandle}.
18 | *
19 | * @since 0.1
20 | */
21 | @SuppressWarnings("PMD.AvoidDuplicateLiterals")
22 | final class InternalExceptionHandleTest {
23 |
24 | @Test
25 | void translatesException() {
26 | final CompletableFuture future = CompletableFuture.runAsync(Assertions::fail);
27 | MatcherAssert.assertThat(
28 | Assertions.assertThrows(
29 | ExecutionException.class,
30 | future.handle(
31 | new InternalExceptionHandle<>(
32 | AssertionError.class,
33 | IllegalStateException::new
34 | )
35 | )
36 | .thenCompose(Function.identity())
37 | .toCompletableFuture()
38 | ::get
39 | ),
40 | Matchers.hasProperty("cause", Matchers.isA(IllegalStateException.class))
41 | );
42 | }
43 |
44 | @Test
45 | void wrapsWithArtipieExceptionIfUnmatched() {
46 | final CompletableFuture future = CompletableFuture.runAsync(Assertions::fail);
47 | MatcherAssert.assertThat(
48 | Assertions.assertThrows(
49 | ExecutionException.class,
50 | future.handle(
51 | new InternalExceptionHandle<>(
52 | NullPointerException.class,
53 | IllegalStateException::new
54 | )
55 | )
56 | .thenCompose(Function.identity())
57 | .toCompletableFuture()
58 | ::get
59 | ),
60 | Matchers.hasProperty("cause", Matchers.isA(ArtipieIOException.class))
61 | );
62 | }
63 |
64 | @Test
65 | void returnsValueIfNoErrorOccurs() throws ExecutionException, InterruptedException {
66 | final CompletableFuture