org.apache.bookkeeper.meta.MockLedgerManager.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.bookkeeper.meta.MockLedgerManager.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */
package org.apache.bookkeeper.meta;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.bookkeeper.client.BKException;
import org.apache.bookkeeper.client.api.LedgerMetadata;
import org.apache.bookkeeper.common.concurrent.FutureUtils;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.LedgerMetadataListener;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.Processor;

import org.apache.bookkeeper.versioning.LongVersion;
import org.apache.bookkeeper.versioning.Version;
import org.apache.bookkeeper.versioning.Versioned;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.zookeeper.AsyncCallback;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Mock implementation of Ledger Manager.
 */
public class MockLedgerManager implements LedgerManager {
    static final Logger LOG = LoggerFactory.getLogger(MockLedgerManager.class);

    /**
     * Hook for injecting errors or delays.
     */
    public interface Hook {
        CompletableFuture<Void> runHook(long ledgerId, LedgerMetadata metadata);
    }

    final Map<Long, Pair<LongVersion, byte[]>> metadataMap;
    final ExecutorService executor;
    final boolean ownsExecutor;
    final LedgerMetadataSerDe serDe;
    private Hook preWriteHook = (ledgerId, metadata) -> FutureUtils.value(null);

    public MockLedgerManager() {
        this(new HashMap<>(), Executors.newSingleThreadExecutor((r) -> new Thread(r, "MockLedgerManager")), true);
    }

    private MockLedgerManager(Map<Long, Pair<LongVersion, byte[]>> metadataMap, ExecutorService executor,
            boolean ownsExecutor) {
        this.metadataMap = metadataMap;
        this.executor = executor;
        this.ownsExecutor = ownsExecutor;
        this.serDe = new LedgerMetadataSerDe();
    }

    public MockLedgerManager newClient() {
        return new MockLedgerManager(metadataMap, executor, false);
    }

    private Versioned<LedgerMetadata> readMetadata(long ledgerId) throws Exception {
        Pair<LongVersion, byte[]> pair = metadataMap.get(ledgerId);
        if (pair == null) {
            return null;
        } else {
            return new Versioned<>(serDe.parseConfig(pair.getRight(), Optional.empty()), pair.getLeft());
        }
    }

    public void setPreWriteHook(Hook hook) {
        this.preWriteHook = hook;
    }

    public void executeCallback(Runnable r) {
        r.run();
    }

    @Override
    public CompletableFuture<Versioned<LedgerMetadata>> createLedgerMetadata(long ledgerId,
            LedgerMetadata metadata) {
        CompletableFuture<Versioned<LedgerMetadata>> promise = new CompletableFuture<>();
        executor.submit(() -> {
            if (metadataMap.containsKey(ledgerId)) {
                executeCallback(() -> promise.completeExceptionally(new BKException.BKLedgerExistException()));
            } else {
                try {
                    metadataMap.put(ledgerId, Pair.of(new LongVersion(0L), serDe.serialize(metadata)));
                    Versioned<LedgerMetadata> readBack = readMetadata(ledgerId);
                    executeCallback(() -> promise.complete(readBack));
                } catch (Exception e) {
                    LOG.error("Error reading back written metadata", e);
                    executeCallback(() -> promise.completeExceptionally(new BKException.MetaStoreException()));
                }
            }
        });
        return promise;
    }

    @Override
    public CompletableFuture<Void> removeLedgerMetadata(long ledgerId, Version version) {
        return CompletableFuture.completedFuture(null);
    }

    @Override
    public CompletableFuture<Versioned<LedgerMetadata>> readLedgerMetadata(long ledgerId) {
        CompletableFuture<Versioned<LedgerMetadata>> promise = new CompletableFuture<>();
        executor.submit(() -> {
            try {
                Versioned<LedgerMetadata> metadata = readMetadata(ledgerId);
                if (metadata == null) {
                    executeCallback(
                            () -> promise.completeExceptionally(new BKException.BKNoSuchLedgerExistsException()));
                } else {
                    executeCallback(() -> promise.complete(metadata));
                }
            } catch (Exception e) {
                LOG.error("Error reading metadata", e);
                executeCallback(() -> promise.completeExceptionally(new BKException.MetaStoreException()));
            }
        });
        return promise;
    }

    @Override
    public CompletableFuture<Versioned<LedgerMetadata>> writeLedgerMetadata(long ledgerId, LedgerMetadata metadata,
            Version currentVersion) {
        CompletableFuture<Versioned<LedgerMetadata>> promise = new CompletableFuture<>();
        preWriteHook.runHook(ledgerId, metadata).thenComposeAsync((ignore) -> {
            try {
                Versioned<LedgerMetadata> oldMetadata = readMetadata(ledgerId);
                if (oldMetadata == null) {
                    return FutureUtils.exception(new BKException.BKNoSuchLedgerExistsException());
                } else if (!oldMetadata.getVersion().equals(currentVersion)) {
                    return FutureUtils.exception(new BKException.BKMetadataVersionException());
                } else {
                    LongVersion oldVersion = (LongVersion) oldMetadata.getVersion();
                    metadataMap.put(ledgerId,
                            Pair.of(new LongVersion(oldVersion.getLongVersion() + 1), serDe.serialize(metadata)));
                    Versioned<LedgerMetadata> readBack = readMetadata(ledgerId);
                    return FutureUtils.value(readBack);
                }
            } catch (Exception e) {
                LOG.error("Error writing metadata", e);
                return FutureUtils.exception(e);
            }
        }, executor).whenComplete((res, ex) -> {
            if (ex != null) {
                Throwable cause = (ex instanceof CompletionException) ? ex.getCause() : ex;
                executeCallback(() -> promise.completeExceptionally(cause));
            } else {
                executeCallback(() -> promise.complete(res));
            }
        });
        return promise;
    }

    @Override
    public void registerLedgerMetadataListener(long ledgerId, LedgerMetadataListener listener) {
    }

    @Override
    public void unregisterLedgerMetadataListener(long ledgerId, LedgerMetadataListener listener) {
    }

    @Override
    public void asyncProcessLedgers(Processor<Long> processor, AsyncCallback.VoidCallback finalCb, Object context,
            int successRc, int failureRc) {
    }

    @Override
    public LedgerRangeIterator getLedgerRanges(long zkOpTimeoutSec) {
        return null;
    }

    @Override
    public void close() {
        if (ownsExecutor) {
            executor.shutdownNow();
        }
    }

}