001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.util;
019
020import java.util.HashSet;
021import java.util.Set;
022import org.apache.hadoop.conf.Configuration;
023import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
024import org.apache.yetus.audience.InterfaceAudience;
025
026import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
027import org.apache.hbase.thirdparty.com.google.common.base.Strings;
028
029/**
030 * Helper class for coprocessor host when configuration changes.
031 */
032@InterfaceAudience.Private
033public final class CoprocessorConfigurationUtil {
034
035  private CoprocessorConfigurationUtil() {
036  }
037
038  /**
039   * Check configuration change by comparing current loaded coprocessors with configuration values.
040   * This method is useful when the configuration object has been updated but we need to determine
041   * if coprocessor configuration has actually changed compared to what's currently loaded.
042   * <p>
043   * <b>Note:</b> This method only detects changes in the set of coprocessor class names. It does
044   * <b>not</b> detect changes to priority or path for coprocessors that are already loaded with the
045   * same class name. If you need to update the priority or path of an existing coprocessor, you
046   * must restart the region/regionserver/master.
047   * @param coprocessorHost  the coprocessor host to check current loaded coprocessors (can be null)
048   * @param conf             the configuration to check
049   * @param configurationKey the configuration keys to check
050   * @return true if configuration has changed, false otherwise
051   */
052  public static boolean checkConfigurationChange(CoprocessorHost<?, ?> coprocessorHost,
053    Configuration conf, String... configurationKey) {
054    Preconditions.checkArgument(configurationKey != null, "Configuration Key(s) must be provided");
055    Preconditions.checkArgument(conf != null, "Configuration must be provided");
056
057    if (
058      !conf.getBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY,
059        CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED)
060    ) {
061      return false;
062    }
063
064    if (coprocessorHost == null) {
065      // If no coprocessor host exists, check if any coprocessors are now configured
066      return hasCoprocessorsConfigured(conf, configurationKey);
067    }
068
069    // Get currently loaded coprocessor class names
070    Set<String> currentlyLoaded = coprocessorHost.getCoprocessorClassNames();
071
072    // Get coprocessor class names from configuration
073    // Only class names are compared; priority and path changes are not detected
074    Set<String> configuredClasses = new HashSet<>();
075    for (String key : configurationKey) {
076      String[] classes = conf.getStrings(key);
077      if (classes != null) {
078        for (String className : classes) {
079          // Handle the className|priority|path format
080          String[] classNameToken = className.split("\\|");
081          String actualClassName = classNameToken[0].trim();
082          if (!Strings.isNullOrEmpty(actualClassName)) {
083            configuredClasses.add(actualClassName);
084          }
085        }
086      }
087    }
088
089    // Compare the two sets
090    return !currentlyLoaded.equals(configuredClasses);
091  }
092
093  /**
094   * Helper method to check if there are any coprocessors configured.
095   */
096  private static boolean hasCoprocessorsConfigured(Configuration conf, String... configurationKey) {
097    for (String key : configurationKey) {
098      String[] coprocessors = conf.getStrings(key);
099      if (coprocessors != null && coprocessors.length > 0) {
100        return true;
101      }
102    }
103    return false;
104  }
105}