/** * 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.replication; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import org.apache.bookkeeper.client.BKException; import org.apache.bookkeeper.client.LedgerMetadata; import org.apache.bookkeeper.meta.LedgerManager; import org.apache.bookkeeper.net.BookieSocketAddress; import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback; import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.Processor; import org.apache.bookkeeper.replication.ReplicationException.BKAuditException; import org.apache.zookeeper.AsyncCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Preparing bookie vs its corresponding ledgers. This will always look up the * ledgermanager for ledger metadata and will generate indexes. */ public class BookieLedgerIndexer { private static final Logger LOG = LoggerFactory.getLogger(BookieLedgerIndexer.class); private final LedgerManager ledgerManager; public BookieLedgerIndexer(LedgerManager ledgerManager) { this.ledgerManager = ledgerManager; } /** * Generating bookie vs its ledgers map by reading all the ledgers in each * bookie and parsing its metadata. * * @return bookie2ledgersMap map of bookie vs ledgers * @throws BKAuditException * exception while getting bookie-ledgers */ public Map<String, Set<Long>> getBookieToLedgerIndex() throws BKAuditException { // bookie vs ledgers map final ConcurrentHashMap<String, Set<Long>> bookie2ledgersMap = new ConcurrentHashMap<String, Set<Long>>(); final CountDownLatch ledgerCollectorLatch = new CountDownLatch(1); Processor<Long> ledgerProcessor = new Processor<Long>() { @Override public void process(final Long ledgerId, final AsyncCallback.VoidCallback iterCallback) { GenericCallback<LedgerMetadata> genericCallback = new GenericCallback<LedgerMetadata>() { @Override public void operationComplete(final int rc, LedgerMetadata ledgerMetadata) { if (rc == BKException.Code.OK) { for (Map.Entry<Long, ArrayList<BookieSocketAddress>> ensemble : ledgerMetadata .getEnsembles().entrySet()) { for (BookieSocketAddress bookie : ensemble .getValue()) { putLedger(bookie2ledgersMap, bookie.toString(), ledgerId); } } } else { LOG.warn("Unable to read the ledger:" + ledgerId + " information"); } iterCallback.processResult(rc, null, null); } }; ledgerManager.readLedgerMetadata(ledgerId, genericCallback); } }; // Reading the result after processing all the ledgers final List<Integer> resultCode = new ArrayList<Integer>(1); ledgerManager.asyncProcessLedgers(ledgerProcessor, new AsyncCallback.VoidCallback() { @Override public void processResult(int rc, String s, Object obj) { resultCode.add(rc); ledgerCollectorLatch.countDown(); } }, null, BKException.Code.OK, BKException.Code.ReadException); try { ledgerCollectorLatch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new BKAuditException( "Exception while getting the bookie-ledgers", e); } if (!resultCode.contains(BKException.Code.OK)) { throw new BKAuditException( "Exception while getting the bookie-ledgers", BKException .create(resultCode.get(0))); } return bookie2ledgersMap; } private void putLedger(ConcurrentHashMap<String, Set<Long>> bookie2ledgersMap, String bookie, long ledgerId) { Set<Long> ledgers = bookie2ledgersMap.get(bookie); // creates an empty list and add to bookie for keeping its ledgers if (ledgers == null) { ledgers = Collections.synchronizedSet(new HashSet<Long>()); Set<Long> oldLedgers = bookie2ledgersMap.putIfAbsent(bookie, ledgers); if (oldLedgers != null) { ledgers = oldLedgers; } } ledgers.add(ledgerId); } }