| /* |
| * 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.iotdb.confignode.persistence.schema; |
| |
| import org.apache.iotdb.common.rpc.thrift.TSchemaNode; |
| import org.apache.iotdb.commons.conf.CommonDescriptor; |
| import org.apache.iotdb.commons.exception.IllegalPathException; |
| import org.apache.iotdb.commons.exception.MetadataException; |
| import org.apache.iotdb.commons.path.PartialPath; |
| import org.apache.iotdb.commons.path.PathPatternTree; |
| import org.apache.iotdb.commons.schema.node.role.IDatabaseMNode; |
| import org.apache.iotdb.commons.schema.node.utils.IMNodeFactory; |
| import org.apache.iotdb.commons.schema.node.utils.IMNodeIterator; |
| import org.apache.iotdb.commons.utils.ThriftConfigNodeSerDeUtils; |
| import org.apache.iotdb.confignode.persistence.schema.mnode.IConfigMNode; |
| import org.apache.iotdb.confignode.persistence.schema.mnode.factory.ConfigMNodeFactory; |
| import org.apache.iotdb.db.exception.metadata.DatabaseAlreadySetException; |
| import org.apache.iotdb.db.exception.metadata.DatabaseNotSetException; |
| import org.apache.iotdb.db.exception.metadata.PathNotExistException; |
| import org.apache.iotdb.db.schemaengine.schemaregion.mtree.traverser.collector.DatabaseCollector; |
| import org.apache.iotdb.db.schemaengine.schemaregion.mtree.traverser.collector.MNodeAboveDBCollector; |
| import org.apache.iotdb.db.schemaengine.schemaregion.mtree.traverser.collector.MNodeCollector; |
| import org.apache.iotdb.db.schemaengine.schemaregion.mtree.traverser.counter.DatabaseCounter; |
| import org.apache.iotdb.db.schemaengine.schemaregion.utils.MetaFormatUtils; |
| import org.apache.iotdb.tsfile.utils.Pair; |
| import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.Deque; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Stack; |
| import java.util.TreeSet; |
| |
| import static org.apache.iotdb.commons.conf.IoTDBConstant.ONE_LEVEL_PATH_WILDCARD; |
| import static org.apache.iotdb.commons.conf.IoTDBConstant.PATH_ROOT; |
| import static org.apache.iotdb.commons.schema.SchemaConstant.ALL_MATCH_SCOPE; |
| import static org.apache.iotdb.commons.schema.SchemaConstant.ALL_RESULT_NODES; |
| import static org.apache.iotdb.commons.schema.SchemaConstant.ALL_TEMPLATE; |
| import static org.apache.iotdb.commons.schema.SchemaConstant.INTERNAL_MNODE_TYPE; |
| import static org.apache.iotdb.commons.schema.SchemaConstant.NON_TEMPLATE; |
| import static org.apache.iotdb.commons.schema.SchemaConstant.STORAGE_GROUP_MNODE_TYPE; |
| |
| // Since the ConfigMTree is all stored in memory, thus it is not restricted to manage MNode through |
| // MTreeStore. |
| public class ConfigMTree { |
| |
| private final Logger logger = LoggerFactory.getLogger(ConfigMTree.class); |
| |
| private IConfigMNode root; |
| // this store is only used for traverser invoking |
| private final ConfigMTreeStore store; |
| |
| private final IMNodeFactory<IConfigMNode> nodeFactory = ConfigMNodeFactory.getInstance(); |
| |
| public ConfigMTree() throws MetadataException { |
| store = new ConfigMTreeStore(nodeFactory); |
| root = store.getRoot(); |
| } |
| |
| public void clear() { |
| if (store != null) { |
| store.clear(); |
| this.root = store.getRoot(); |
| } |
| } |
| |
| // region database Management |
| |
| /** |
| * CREATE DATABASE. Make sure check seriesPath before setting database |
| * |
| * @param path path |
| */ |
| public void setStorageGroup(PartialPath path) throws MetadataException { |
| String[] nodeNames = path.getNodes(); |
| MetaFormatUtils.checkDatabase(path.getFullPath()); |
| if (nodeNames.length <= 1 || !nodeNames[0].equals(root.getName())) { |
| throw new IllegalPathException(path.getFullPath()); |
| } |
| IConfigMNode cur = root; |
| int i = 1; |
| // e.g., path = root.a.b.sg, create internal nodes for a, b |
| while (i < nodeNames.length - 1) { |
| IConfigMNode temp = store.getChild(cur, nodeNames[i]); |
| if (temp == null) { |
| store.addChild(cur, nodeNames[i], nodeFactory.createInternalMNode(cur, nodeNames[i])); |
| } else if (temp.isDatabase()) { |
| // before create database, check whether the database already exists |
| throw new DatabaseAlreadySetException(temp.getFullPath()); |
| } |
| cur = store.getChild(cur, nodeNames[i]); |
| i++; |
| } |
| |
| // synchronize check and add, we need addChild operation be atomic. |
| // only write operations on mtree will be synchronized |
| synchronized (this) { |
| if (store.hasChild(cur, nodeNames[i])) { |
| // node b has child sg |
| if (store.getChild(cur, nodeNames[i]).isDatabase()) { |
| throw new DatabaseAlreadySetException(path.getFullPath()); |
| } else { |
| throw new DatabaseAlreadySetException(path.getFullPath(), true); |
| } |
| } else { |
| IDatabaseMNode<IConfigMNode> databaseMNode = |
| nodeFactory.createDatabaseMNode(cur, nodeNames[i]); |
| databaseMNode.setDataTTL(CommonDescriptor.getInstance().getConfig().getDefaultTTLInMs()); |
| |
| IConfigMNode result = store.addChild(cur, nodeNames[i], databaseMNode.getAsMNode()); |
| |
| if (result != databaseMNode) { |
| throw new DatabaseAlreadySetException(path.getFullPath(), true); |
| } |
| } |
| } |
| } |
| |
| /** Delete a database */ |
| public void deleteDatabase(PartialPath path) throws MetadataException { |
| IDatabaseMNode<IConfigMNode> databaseMNode = getDatabaseNodeByDatabasePath(path); |
| IConfigMNode cur = databaseMNode.getParent(); |
| // Suppose current system has root.a.b.sg1, root.a.sg2, and delete root.a.b.sg1 |
| // delete the database node sg1 |
| store.deleteChild(cur, databaseMNode.getName()); |
| |
| // delete node a while retain root.a.sg2 |
| while (cur.getParent() != null && cur.getChildren().size() == 0) { |
| cur.getParent().deleteChild(cur.getName()); |
| cur = cur.getParent(); |
| } |
| } |
| |
| /** |
| * Get the database that given path pattern matches or belongs to. |
| * |
| * <p>Suppose we have (root.sg1.d1.s1, root.sg2.d2.s2), refer the following cases: 1. given path |
| * "root.sg1", ("root.sg1") will be returned. 2. given path "root.*", ("root.sg1", "root.sg2") |
| * will be returned. 3. given path "root.*.d1.s1", ("root.sg1", "root.sg2") will be returned. |
| * |
| * @param pathPattern a path pattern or a full path |
| * @return a list contains all databases related to given path |
| */ |
| public List<PartialPath> getBelongedDatabases(PartialPath pathPattern) throws MetadataException { |
| return collectDatabases(pathPattern, ALL_MATCH_SCOPE, false, true); |
| } |
| |
| /** |
| * Get all database that the given path pattern matches. If using prefix match, the path pattern |
| * is used to match prefix path. All timeseries start with the matched prefix path will be |
| * collected. |
| * |
| * @param pathPattern a path pattern or a full path |
| * @param scope traversing scope |
| * @param isPrefixMatch if true, the path pattern is used to match prefix path |
| * @return a list contains all database names under given path pattern |
| */ |
| public List<PartialPath> getMatchedDatabases( |
| PartialPath pathPattern, PathPatternTree scope, boolean isPrefixMatch) |
| throws MetadataException { |
| return collectDatabases(pathPattern, scope, isPrefixMatch, false); |
| } |
| |
| private List<PartialPath> collectDatabases( |
| PartialPath pathPattern, |
| PathPatternTree scope, |
| boolean isPrefixMatch, |
| boolean collectInternal) |
| throws MetadataException { |
| List<PartialPath> result = new LinkedList<>(); |
| try (DatabaseCollector<?, ?> collector = |
| new DatabaseCollector<List<PartialPath>, IConfigMNode>( |
| root, pathPattern, store, isPrefixMatch, scope) { |
| |
| @Override |
| protected void collectDatabase(IDatabaseMNode<IConfigMNode> node) { |
| result.add(node.getPartialPath()); |
| } |
| }) { |
| collector.setCollectInternal(collectInternal); |
| collector.traverse(); |
| } |
| return result; |
| } |
| |
| /** |
| * Get all database names |
| * |
| * @return a list contains all distinct databases |
| */ |
| public List<PartialPath> getAllDatabasePaths() { |
| List<PartialPath> res = new ArrayList<>(); |
| Deque<IConfigMNode> nodeStack = new ArrayDeque<>(); |
| nodeStack.add(root); |
| while (!nodeStack.isEmpty()) { |
| IConfigMNode current = nodeStack.pop(); |
| if (current.isDatabase()) { |
| res.add(current.getPartialPath()); |
| } else { |
| nodeStack.addAll(current.getChildren().values()); |
| } |
| } |
| return res; |
| } |
| |
| /** |
| * Get the count of database matching the given path. If using prefix match, the path pattern is |
| * used to match prefix path. All timeseries start with the matched prefix path will be counted. |
| * |
| * @param pathPattern a path pattern or a full path, may contain wildcard. |
| * @param scope traversing scope |
| * @param isPrefixMatch if true, the path pattern is used to match prefix path |
| */ |
| public int getDatabaseNum(PartialPath pathPattern, PathPatternTree scope, boolean isPrefixMatch) |
| throws MetadataException { |
| try (DatabaseCounter<IConfigMNode> counter = |
| new DatabaseCounter<>(root, pathPattern, store, isPrefixMatch, scope)) { |
| return (int) counter.count(); |
| } |
| } |
| |
| /** |
| * E.g., root.sg is database given [root, sg], if the give path is not a database, throw exception |
| */ |
| public IDatabaseMNode<IConfigMNode> getDatabaseNodeByDatabasePath(PartialPath databasePath) |
| throws MetadataException { |
| String[] nodes = databasePath.getNodes(); |
| if (nodes.length == 0 || !nodes[0].equals(root.getName())) { |
| throw new IllegalPathException(databasePath.getFullPath()); |
| } |
| IConfigMNode cur = root; |
| for (int i = 1; i < nodes.length - 1; i++) { |
| cur = cur.getChild(nodes[i]); |
| if (cur == null) { |
| throw new DatabaseNotSetException(databasePath.getFullPath()); |
| } |
| if (cur.isDatabase()) { |
| throw new DatabaseAlreadySetException(cur.getFullPath()); |
| } |
| } |
| |
| cur = cur.getChild(nodes[nodes.length - 1]); |
| if (cur == null) { |
| throw new DatabaseNotSetException(databasePath.getFullPath()); |
| } |
| if (cur.isDatabase()) { |
| return cur.getAsDatabaseMNode(); |
| } else { |
| throw new DatabaseAlreadySetException(databasePath.getFullPath(), true); |
| } |
| } |
| |
| /** |
| * E.g., root.sg is database given [root, sg], return the MNode of root.sg given [root, sg, |
| * device], return the MNode of root.sg Get database node, the give path don't need to be database |
| * path. |
| */ |
| public IDatabaseMNode<IConfigMNode> getDatabaseNodeByPath(PartialPath path) |
| throws MetadataException { |
| String[] nodes = path.getNodes(); |
| if (nodes.length == 0 || !nodes[0].equals(root.getName())) { |
| throw new IllegalPathException(path.getFullPath()); |
| } |
| IConfigMNode cur = root; |
| for (int i = 1; i < nodes.length; i++) { |
| cur = cur.getChild(nodes[i]); |
| if (cur == null) { |
| break; |
| } |
| if (cur.isDatabase()) { |
| return cur.getAsDatabaseMNode(); |
| } |
| } |
| throw new DatabaseNotSetException(path.getFullPath()); |
| } |
| |
| /** |
| * Check whether the database of given path exists. The given path may be a prefix path of |
| * existing database. |
| * |
| * @param path a full path or a prefix path |
| */ |
| public boolean isDatabaseAlreadySet(PartialPath path) { |
| String[] nodeNames = path.getNodes(); |
| IConfigMNode cur = root; |
| if (!nodeNames[0].equals(root.getName())) { |
| return false; |
| } |
| for (int i = 1; i < nodeNames.length; i++) { |
| if (!store.hasChild(cur, nodeNames[i])) { |
| return false; |
| } |
| cur = store.getChild(cur, nodeNames[i]); |
| if (cur.isDatabase()) { |
| return true; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Check whether the database of given path exists. The given path may be a prefix path of |
| * existing database. if exists will throw MetaException. |
| * |
| * @param path a full path or a prefix path |
| */ |
| public void checkDatabaseAlreadySet(PartialPath path) throws DatabaseAlreadySetException { |
| String[] nodeNames = path.getNodes(); |
| IConfigMNode cur = root; |
| if (!nodeNames[0].equals(root.getName())) { |
| return; |
| } |
| for (int i = 1; i < nodeNames.length; i++) { |
| if (!store.hasChild(cur, nodeNames[i])) { |
| return; |
| } |
| cur = store.getChild(cur, nodeNames[i]); |
| if (cur.isDatabase()) { |
| throw new DatabaseAlreadySetException(cur.getFullPath()); |
| } |
| } |
| throw new DatabaseAlreadySetException(path.getFullPath(), true); |
| } |
| |
| // endregion |
| |
| // region MTree Node Management |
| |
| public IConfigMNode getNodeWithAutoCreate(PartialPath path) throws DatabaseNotSetException { |
| String[] nodeNames = path.getNodes(); |
| IConfigMNode cur = root; |
| IConfigMNode child; |
| boolean hasStorageGroup = false; |
| for (int i = 1; i < nodeNames.length; i++) { |
| child = store.getChild(cur, nodeNames[i]); |
| if (child == null) { |
| if (hasStorageGroup) { |
| child = |
| store.addChild(cur, nodeNames[i], nodeFactory.createInternalMNode(cur, nodeNames[i])); |
| } else { |
| throw new DatabaseNotSetException(path.getFullPath()); |
| } |
| } else if (child.isDatabase()) { |
| hasStorageGroup = true; |
| } |
| |
| cur = child; |
| } |
| return cur; |
| } |
| |
| /** |
| * Get all paths of nodes in the given level matching the given path. If using prefix match, the |
| * path pattern is used to match prefix path. |
| */ |
| public Pair<List<PartialPath>, Set<PartialPath>> getNodesListInGivenLevel( |
| PartialPath pathPattern, int nodeLevel, boolean isPrefixMatch, PathPatternTree scope) |
| throws MetadataException { |
| List<PartialPath> result = new LinkedList<>(); |
| try (MNodeAboveDBCollector<Void, IConfigMNode> collector = |
| new MNodeAboveDBCollector<Void, IConfigMNode>( |
| root, pathPattern, store, isPrefixMatch, scope) { |
| @Override |
| protected Void collectMNode(IConfigMNode node) { |
| result.add(getPartialPathFromRootToNode(node)); |
| return null; |
| } |
| }) { |
| |
| collector.setTargetLevel(nodeLevel); |
| collector.traverse(); |
| return new Pair<>(result, collector.getInvolvedDatabaseMNodes()); |
| } |
| } |
| |
| /** |
| * Get child node path in the next level of the given path pattern. This method only count in |
| * nodes above database. Nodes below database, including database node will be counted by certain |
| * MTreeBelowSG. |
| * |
| * <p>give pathPattern and the child nodes is those matching pathPattern.* |
| * |
| * <p>e.g., MTree has [root.a.sg1.d1.s1, root.b.sg1.d1.s2, root.c.sg1.d2.s1] given path = root |
| * return [root.a, root.b] |
| * |
| * @param pathPattern The given path |
| * @param scope traversing scope |
| * @return All child nodes' seriesPath(s) of given seriesPath. |
| */ |
| public Pair<Set<TSchemaNode>, Set<PartialPath>> getChildNodePathInNextLevel( |
| PartialPath pathPattern, PathPatternTree scope) throws MetadataException { |
| Set<TSchemaNode> result = new TreeSet<>(); |
| try (MNodeAboveDBCollector<Void, IConfigMNode> collector = |
| new MNodeAboveDBCollector<Void, IConfigMNode>( |
| root, pathPattern.concatNode(ONE_LEVEL_PATH_WILDCARD), store, false, scope) { |
| @Override |
| protected Void collectMNode(IConfigMNode node) { |
| result.add( |
| new TSchemaNode( |
| getPartialPathFromRootToNode(node).getFullPath(), |
| node.getMNodeType().getNodeType())); |
| return null; |
| } |
| }) { |
| collector.traverse(); |
| return new Pair<>(result, collector.getInvolvedDatabaseMNodes()); |
| } catch (IllegalPathException e) { |
| throw new IllegalPathException(pathPattern.getFullPath()); |
| } |
| } |
| |
| // endregion |
| |
| // region Template Management |
| |
| /** |
| * check whether there is template on given path and the subTree has template return true, |
| * otherwise false |
| */ |
| public void checkTemplateOnPath(PartialPath path) throws MetadataException { |
| String[] nodeNames = path.getNodes(); |
| IConfigMNode cur = root; |
| IConfigMNode child; |
| |
| if (cur.getSchemaTemplateId() != NON_TEMPLATE) { |
| throw new MetadataException("Template already exists on " + cur.getFullPath()); |
| } |
| |
| for (int i = 1; i < nodeNames.length; i++) { |
| child = cur.getChild(nodeNames[i]); |
| if (child == null) { |
| return; |
| } |
| cur = child; |
| if (cur.getSchemaTemplateId() != NON_TEMPLATE) { |
| throw new MetadataException("Template already exists on " + cur.getFullPath()); |
| } |
| if (cur.isMeasurement()) { |
| return; |
| } |
| } |
| |
| checkTemplateOnSubtree(cur); |
| } |
| |
| // traverse all the descendant of the given path node |
| private void checkTemplateOnSubtree(IConfigMNode node) throws MetadataException { |
| if (node.isMeasurement()) { |
| return; |
| } |
| IConfigMNode child; |
| IMNodeIterator<IConfigMNode> iterator = store.getChildrenIterator(node); |
| while (iterator.hasNext()) { |
| child = iterator.next(); |
| |
| if (child.isMeasurement()) { |
| continue; |
| } |
| if (child.getSchemaTemplateId() != NON_TEMPLATE) { |
| throw new MetadataException("Template already exists on " + child.getFullPath()); |
| } |
| checkTemplateOnSubtree(child); |
| } |
| } |
| |
| public List<String> getPathsSetOnTemplate( |
| int templateId, PathPatternTree scope, boolean filterPreUnset) throws MetadataException { |
| List<String> resSet = new ArrayList<>(); |
| try (MNodeCollector<Void, IConfigMNode> collector = |
| new MNodeCollector<Void, IConfigMNode>( |
| root, new PartialPath(ALL_RESULT_NODES), store, false, scope) { |
| @Override |
| protected boolean acceptFullMatchedNode(IConfigMNode node) { |
| if (super.acceptFullMatchedNode(node)) { |
| // if node not set template, go on traversing |
| if (node.getSchemaTemplateId() != NON_TEMPLATE) { |
| if (filterPreUnset && node.isSchemaTemplatePreUnset()) { |
| // filter the pre unset template |
| return false; |
| } |
| // if set template, and equals to target or target for all, add to result |
| return templateId == ALL_TEMPLATE || templateId == node.getSchemaTemplateId(); |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| protected Void collectMNode(IConfigMNode node) { |
| resSet.add(node.getFullPath()); |
| return null; |
| } |
| |
| @Override |
| protected boolean shouldVisitSubtreeOfFullMatchedNode(IConfigMNode node) { |
| // descendants of the node cannot set another template, exit from this branch |
| return (node.getSchemaTemplateId() == NON_TEMPLATE) |
| && super.shouldVisitSubtreeOfFullMatchedNode(node); |
| } |
| |
| @Override |
| protected boolean shouldVisitSubtreeOfInternalMatchedNode(IConfigMNode node) { |
| // descendants of the node cannot set another template, exit from this branch |
| return (node.getSchemaTemplateId() == NON_TEMPLATE) |
| && super.shouldVisitSubtreeOfFullMatchedNode(node); |
| } |
| }) { |
| collector.traverse(); |
| } |
| return resSet; |
| } |
| |
| /** This method returns the templateId set on paths covered by input path pattern. */ |
| public Map<Integer, Set<PartialPath>> getTemplateSetInfo(PartialPath pathPattern) |
| throws MetadataException { |
| Map<Integer, Set<PartialPath>> result = new HashMap<>(); |
| try (MNodeCollector<Void, IConfigMNode> collector = |
| new MNodeCollector<Void, IConfigMNode>(root, pathPattern, store, false, ALL_MATCH_SCOPE) { |
| @Override |
| protected boolean acceptFullMatchedNode(IConfigMNode node) { |
| return (node.getSchemaTemplateId() != NON_TEMPLATE) |
| || super.acceptFullMatchedNode(node); |
| } |
| |
| @Override |
| protected boolean acceptInternalMatchedNode(IConfigMNode node) { |
| return (node.getSchemaTemplateId() != NON_TEMPLATE) |
| || super.acceptInternalMatchedNode(node); |
| } |
| |
| @Override |
| protected Void collectMNode(IConfigMNode node) { |
| if (node.getSchemaTemplateId() != NON_TEMPLATE && !node.isSchemaTemplatePreUnset()) { |
| result |
| .computeIfAbsent(node.getSchemaTemplateId(), k -> new HashSet<>()) |
| .add(getPartialPathFromRootToNode(node)); |
| } |
| return null; |
| } |
| |
| @Override |
| protected boolean shouldVisitSubtreeOfFullMatchedNode(IConfigMNode node) { |
| // descendants of the node cannot set another template, exit from this branch |
| return (node.getSchemaTemplateId() == NON_TEMPLATE) |
| && super.shouldVisitSubtreeOfFullMatchedNode(node); |
| } |
| |
| @Override |
| protected boolean shouldVisitSubtreeOfInternalMatchedNode(IConfigMNode node) { |
| // descendants of the node cannot set another template, exit from this branch |
| return (node.getSchemaTemplateId() == NON_TEMPLATE) |
| && super.shouldVisitSubtreeOfFullMatchedNode(node); |
| } |
| }) { |
| collector.traverse(); |
| } |
| return result; |
| } |
| |
| public void setTemplate(int templateId, PartialPath templateSetPath) throws MetadataException { |
| getNodeWithAutoCreate(templateSetPath).setSchemaTemplateId(templateId); |
| } |
| |
| public void preUnsetTemplate(int templateId, PartialPath path) throws MetadataException { |
| getNodeSetTemplate(templateId, path).preUnsetSchemaTemplate(); |
| } |
| |
| public void rollbackUnsetTemplate(int templateId, PartialPath path) throws MetadataException { |
| getNodeSetTemplate(templateId, path).rollbackUnsetSchemaTemplate(); |
| } |
| |
| public void unsetTemplate(int templateId, PartialPath path) throws MetadataException { |
| getNodeSetTemplate(templateId, path).unsetSchemaTemplate(); |
| } |
| |
| private IConfigMNode getNodeSetTemplate(int templateId, PartialPath path) |
| throws MetadataException { |
| String[] nodeNames = path.getNodes(); |
| IConfigMNode cur = root; |
| for (int i = 1; i < nodeNames.length; i++) { |
| cur = cur.getChild(nodeNames[i]); |
| if (cur == null) { |
| throw new PathNotExistException(path.getFullPath()); |
| } |
| } |
| if (cur.getSchemaTemplateId() != templateId) { |
| throw new MetadataException( |
| String.format("Template %s is not set on path %s", templateId, path)); |
| } |
| return cur; |
| } |
| |
| // endregion |
| |
| // region Serialization and Deserialization |
| |
| public void serialize(OutputStream outputStream) throws IOException { |
| serializeConfigBasicMNode(this.root, outputStream); |
| } |
| |
| private void serializeConfigBasicMNode(IConfigMNode node, OutputStream outputStream) |
| throws IOException { |
| serializeChildren(node, outputStream); |
| |
| ReadWriteIOUtils.write(INTERNAL_MNODE_TYPE, outputStream); |
| ReadWriteIOUtils.write(node.getName(), outputStream); |
| ReadWriteIOUtils.write(node.getSchemaTemplateId(), outputStream); |
| ReadWriteIOUtils.write(node.getChildren().size(), outputStream); |
| } |
| |
| private void serializeChildren(IConfigMNode node, OutputStream outputStream) throws IOException { |
| for (IConfigMNode child : node.getChildren().values()) { |
| if (child.isDatabase()) { |
| serializeDatabaseNode(child.getAsDatabaseMNode(), outputStream); |
| } else { |
| serializeConfigBasicMNode(child, outputStream); |
| } |
| } |
| } |
| |
| private void serializeDatabaseNode( |
| IDatabaseMNode<IConfigMNode> storageGroupNode, OutputStream outputStream) throws IOException { |
| serializeChildren(storageGroupNode.getAsMNode(), outputStream); |
| |
| ReadWriteIOUtils.write(STORAGE_GROUP_MNODE_TYPE, outputStream); |
| ReadWriteIOUtils.write(storageGroupNode.getName(), outputStream); |
| ReadWriteIOUtils.write(storageGroupNode.getAsMNode().getSchemaTemplateId(), outputStream); |
| ThriftConfigNodeSerDeUtils.serializeTDatabaseSchema( |
| storageGroupNode.getAsMNode().getDatabaseSchema(), outputStream); |
| } |
| |
| public void deserialize(InputStream inputStream) throws IOException { |
| byte type = ReadWriteIOUtils.readByte(inputStream); |
| |
| String name = null; |
| int childNum = 0; |
| Stack<Pair<IConfigMNode, Boolean>> stack = new Stack<>(); |
| IConfigMNode databaseMNode; |
| IConfigMNode internalMNode; |
| |
| if (type == STORAGE_GROUP_MNODE_TYPE) { |
| databaseMNode = deserializeDatabaseMNode(inputStream); |
| name = databaseMNode.getName(); |
| stack.push(new Pair<>(databaseMNode, true)); |
| } else { |
| internalMNode = deserializeInternalMNode(inputStream); |
| childNum = ReadWriteIOUtils.readInt(inputStream); |
| name = internalMNode.getName(); |
| stack.push(new Pair<>(internalMNode, false)); |
| } |
| |
| while (!PATH_ROOT.equals(name)) { |
| type = ReadWriteIOUtils.readByte(inputStream); |
| switch (type) { |
| case INTERNAL_MNODE_TYPE: |
| internalMNode = deserializeInternalMNode(inputStream); |
| childNum = ReadWriteIOUtils.readInt(inputStream); |
| boolean hasDB = false; |
| while (childNum > 0) { |
| hasDB = stack.peek().right; |
| internalMNode.addChild(stack.pop().left); |
| childNum--; |
| } |
| stack.push(new Pair<>(internalMNode, hasDB)); |
| name = internalMNode.getName(); |
| break; |
| case STORAGE_GROUP_MNODE_TYPE: |
| databaseMNode = deserializeDatabaseMNode(inputStream).getAsMNode(); |
| childNum = 0; |
| while (!stack.isEmpty() && !stack.peek().right) { |
| databaseMNode.addChild(stack.pop().left); |
| } |
| stack.push(new Pair<>(databaseMNode, true)); |
| name = databaseMNode.getName(); |
| break; |
| default: |
| logger.error("Unrecognized node type. Cannot deserialize MTreeAboveSG from given buffer"); |
| return; |
| } |
| } |
| this.root = stack.peek().left; |
| } |
| |
| private IConfigMNode deserializeInternalMNode(InputStream inputStream) throws IOException { |
| IConfigMNode basicMNode = |
| nodeFactory.createInternalMNode(null, ReadWriteIOUtils.readString(inputStream)); |
| basicMNode.setSchemaTemplateId(ReadWriteIOUtils.readInt(inputStream)); |
| return basicMNode; |
| } |
| |
| private IConfigMNode deserializeDatabaseMNode(InputStream inputStream) throws IOException { |
| IDatabaseMNode<IConfigMNode> databaseMNode = |
| nodeFactory.createDatabaseMNode(null, ReadWriteIOUtils.readString(inputStream)); |
| databaseMNode.getAsMNode().setSchemaTemplateId(ReadWriteIOUtils.readInt(inputStream)); |
| databaseMNode |
| .getAsMNode() |
| .setDatabaseSchema(ThriftConfigNodeSerDeUtils.deserializeTDatabaseSchema(inputStream)); |
| return databaseMNode.getAsMNode(); |
| } |
| |
| // endregion |
| } |