package org.apache.bookkeeper.meta; /** * 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. */ import java.io.IOException; import java.util.List; import org.apache.bookkeeper.util.BookKeeperConstants; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class encapsulates ledger layout information that is persistently stored * in zookeeper. It provides parsing and serialization methods of such information. * */ class LedgerLayout { static final Logger LOG = LoggerFactory.getLogger(LedgerLayout.class); // version of compability layout version public static final int LAYOUT_MIN_COMPAT_VERSION = 1; // version of ledger layout metadata public static final int LAYOUT_FORMAT_VERSION = 2; /** * Read ledger layout from zookeeper * * @param zk ZooKeeper Client * @param ledgersRoot Root of the ledger namespace to check * @return ledger layout, or null if none set in zookeeper */ public static LedgerLayout readLayout(final ZooKeeper zk, final String ledgersRoot) throws IOException, KeeperException { String ledgersLayout = ledgersRoot + "/" + BookKeeperConstants.LAYOUT_ZNODE; try { LedgerLayout layout; try { byte[] layoutData = zk.getData(ledgersLayout, false, null); layout = parseLayout(layoutData); } catch (KeeperException.NoNodeException nne) { return null; } return layout; } catch (InterruptedException ie) { throw new IOException(ie); } } static final String splitter = ":"; static final String lSplitter = "\n"; // ledger manager factory class private String managerFactoryCls; // ledger manager version private int managerVersion; // layout version of how to store layout information private int layoutFormatVersion = LAYOUT_FORMAT_VERSION; /** * Ledger Layout Constructor * * @param managerFactoryCls * Ledger Manager Factory Class * @param managerVersion * Ledger Manager Version * @param layoutFormatVersion * Ledger Layout Format Version */ public LedgerLayout(String managerFactoryCls, int managerVersion) { this(managerFactoryCls, managerVersion, LAYOUT_FORMAT_VERSION); } LedgerLayout(String managerFactoryCls, int managerVersion, int layoutVersion) { this.managerFactoryCls = managerFactoryCls; this.managerVersion = managerVersion; this.layoutFormatVersion = layoutVersion; } /** * Get Ledger Manager Type * * @return ledger manager type * @deprecated replaced by {@link #getManagerFactoryClass()} */ @Deprecated public String getManagerType() { // pre V2 layout store as manager type return this.managerFactoryCls; } /** * Get ledger manager factory class * * @return ledger manager factory class */ public String getManagerFactoryClass() { return this.managerFactoryCls; } public int getManagerVersion() { return this.managerVersion; } /** * Return layout format version * * @return layout format version */ public int getLayoutFormatVersion() { return this.layoutFormatVersion; } /** * Store the ledger layout into zookeeper */ public void store(final ZooKeeper zk, String ledgersRoot, List<ACL> zkAcls) throws IOException, KeeperException, InterruptedException { String ledgersLayout = ledgersRoot + "/" + BookKeeperConstants.LAYOUT_ZNODE; zk.create(ledgersLayout, serialize(), zkAcls, CreateMode.PERSISTENT); } /** * Delete the LAYOUT from zookeeper */ public void delete(final ZooKeeper zk, String ledgersRoot) throws KeeperException, InterruptedException { String ledgersLayout = ledgersRoot + "/" + BookKeeperConstants.LAYOUT_ZNODE; zk.delete(ledgersLayout, -1); } /** * Generates a byte array based on the LedgerLayout object. * * @return byte[] */ private byte[] serialize() throws IOException { String s = new StringBuilder().append(layoutFormatVersion).append(lSplitter) .append(managerFactoryCls).append(splitter).append(managerVersion).toString(); LOG.debug("Serialized layout info: {}", s); return s.getBytes("UTF-8"); } /** * Parses a given byte array and transforms into a LedgerLayout object * * @param bytes * byte array to parse * @param znodeVersion * version of znode * @return LedgerLayout * @throws IOException * if the given byte[] cannot be parsed */ private static LedgerLayout parseLayout(byte[] bytes) throws IOException { String layout = new String(bytes, "UTF-8"); LOG.debug("Parsing Layout: {}", layout); String lines[] = layout.split(lSplitter); try { int layoutFormatVersion = Integer.parseInt(lines[0]); if (LAYOUT_FORMAT_VERSION < layoutFormatVersion || LAYOUT_MIN_COMPAT_VERSION > layoutFormatVersion) { throw new IOException("Metadata version not compatible. Expected " + LAYOUT_FORMAT_VERSION + ", but got " + layoutFormatVersion); } if (lines.length < 2) { throw new IOException("Ledger manager and its version absent from layout: " + layout); } String[] parts = lines[1].split(splitter); if (parts.length != 2) { throw new IOException("Invalid Ledger Manager defined in layout : " + layout); } // ledger manager factory class String managerFactoryCls = parts[0]; // ledger manager version int managerVersion = Integer.parseInt(parts[1]); return new LedgerLayout(managerFactoryCls, managerVersion, layoutFormatVersion); } catch (NumberFormatException e) { throw new IOException("Could not parse layout '" + layout + "'", e); } } @Override public boolean equals(Object obj) { if (null == obj) { return false; } if (!(obj instanceof LedgerLayout)) { return false; } LedgerLayout other = (LedgerLayout)obj; return managerFactoryCls.equals(other.managerFactoryCls) && managerVersion == other.managerVersion; } @Override public int hashCode() { return (managerFactoryCls + managerVersion).hashCode(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("LV").append(layoutFormatVersion).append(":") .append(",Type:").append(managerFactoryCls).append(":") .append(managerVersion); return sb.toString(); } }