/* * 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.nifi.controller.cluster; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.util.FormatUtils; import org.apache.nifi.util.NiFiProperties; import org.apache.zookeeper.common.PathUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZooKeeperClientConfig { private static final Logger logger = LoggerFactory.getLogger(ZooKeeperClientConfig.class); private static final Pattern PORT_PATTERN = Pattern.compile("[0-9]{1,5}"); private final String connectString; private final int sessionTimeoutMillis; private final int connectionTimeoutMillis; private final String rootPath; private ZooKeeperClientConfig(String connectString, int sessionTimeoutMillis, int connectionTimeoutMillis, String rootPath) { this.connectString = connectString; this.sessionTimeoutMillis = sessionTimeoutMillis; this.connectionTimeoutMillis = connectionTimeoutMillis; this.rootPath = rootPath.endsWith("/") ? rootPath.substring(0, rootPath.length() - 1) : rootPath; } public String getConnectString() { return connectString; } public int getSessionTimeoutMillis() { return sessionTimeoutMillis; } public int getConnectionTimeoutMillis() { return connectionTimeoutMillis; } public String getRootPath() { return rootPath; } public String resolvePath(final String path) { if (path.startsWith("/")) { return rootPath + path; } return rootPath + "/" + path; } public static ZooKeeperClientConfig createConfig(final NiFiProperties nifiProperties) { final String connectString = nifiProperties.getProperty(NiFiProperties.ZOOKEEPER_CONNECT_STRING); if (connectString == null || connectString.trim().isEmpty()) { throw new IllegalStateException("The '" + NiFiProperties.ZOOKEEPER_CONNECT_STRING + "' property is not set in nifi.properties"); } final String cleanedConnectString = cleanConnectString(connectString); if (cleanedConnectString.isEmpty()) { throw new IllegalStateException("The '" + NiFiProperties.ZOOKEEPER_CONNECT_STRING + "' property is set in nifi.properties but needs to be in pairs of host:port separated by commas"); } final long sessionTimeoutMs = getTimePeriod(nifiProperties, NiFiProperties.ZOOKEEPER_SESSION_TIMEOUT, NiFiProperties.DEFAULT_ZOOKEEPER_SESSION_TIMEOUT); final long connectionTimeoutMs = getTimePeriod(nifiProperties, NiFiProperties.ZOOKEEPER_CONNECT_TIMEOUT, NiFiProperties.DEFAULT_ZOOKEEPER_CONNECT_TIMEOUT); final String rootPath = nifiProperties.getProperty(NiFiProperties.ZOOKEEPER_ROOT_NODE, NiFiProperties.DEFAULT_ZOOKEEPER_ROOT_NODE); try { PathUtils.validatePath(rootPath); } catch (final IllegalArgumentException e) { throw new IllegalArgumentException("The '" + NiFiProperties.ZOOKEEPER_ROOT_NODE + "' property in nifi.properties is set to an illegal value: " + rootPath); } return new ZooKeeperClientConfig(cleanedConnectString, (int) sessionTimeoutMs, (int) connectionTimeoutMs, rootPath); } private static int getTimePeriod(final NiFiProperties nifiProperties, final String propertyName, final String defaultValue) { final String timeout = nifiProperties.getProperty(propertyName, defaultValue); try { return (int) FormatUtils.getTimeDuration(timeout, TimeUnit.MILLISECONDS); } catch (final Exception e) { logger.warn("Value of '" + propertyName + "' property is set to '" + timeout + "', which is not a valid time period. Using default of " + defaultValue); return (int) FormatUtils.getTimeDuration(defaultValue, TimeUnit.MILLISECONDS); } } /** * Takes a given connect string and splits it by ',' character. For each * split result trims whitespace then splits by ':' character. For each * secondary split if a single value is returned it is trimmed and then the * default zookeeper 2181 is append by adding ":2181". If two values are * returned then the second value is evaluated to ensure it contains only * digits and if not then the entry is in error and exception is raised. * If more than two values are * returned the entry is in error and an exception is raised. * Each entry is trimmed and if empty the * entry is skipped. After all splits are cleaned then they are all appended * back together demarcated by "," and the full string is returned. * * @param connectString the string to clean * @return cleaned connect string guaranteed to be non null but could be * empty * @throws IllegalStateException if any portions could not be cleaned/parsed */ public static String cleanConnectString(final String connectString) { final String nospaces = StringUtils.deleteWhitespace(connectString); final String hostPortPairs[] = StringUtils.split(nospaces, ",", 100); final List<String> cleanedEntries = new ArrayList<>(hostPortPairs.length); for (final String pair : hostPortPairs) { final String pairSplits[] = StringUtils.split(pair, ":", 3); if (pairSplits.length > 2 || pairSplits[0].isEmpty()) { throw new IllegalStateException("Invalid host:port pair entry '" + pair + "' in nifi.properties " + NiFiProperties.ZOOKEEPER_CONNECT_STRING + "' property"); } if (pairSplits.length == 1) { cleanedEntries.add(pairSplits[0] + ":2181"); }else{ if(PORT_PATTERN.matcher(pairSplits[1]).matches()){ cleanedEntries.add(pairSplits[0] + ":" + pairSplits[1]); }else{ throw new IllegalStateException("The port specified in this pair must be 1 to 5 digits only but was '" + pair + "' in nifi.properties " + NiFiProperties.ZOOKEEPER_CONNECT_STRING + "' property"); } } } return StringUtils.join(cleanedEntries, ","); } }