content: /*
   * 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.kafka.common.utils;
  
  import org.apache.kafka.common.KafkaException;
  
  import java.io.File;
  import java.io.IOException;
  import java.net.URL;
  import java.net.URLClassLoader;
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.Locale;
  import java.util.NoSuchElementException;
  
  /**
   * A class loader that looks for classes and resources in a specified class path first, before delegating to its parent
   * class loader.
   */
  public class ChildFirstClassLoader extends URLClassLoader {
      static {
          ClassLoader.registerAsParallelCapable();
      }
  
      /**
       * @param classPath Class path string
       * @param parent    The parent classloader. If the required class / resource cannot be found in the given classPath,
       *                  this classloader will be used to find the class / resource.
       */
      public ChildFirstClassLoader(String classPath, ClassLoader parent) {
          super(classpathToURLs(classPath), parent);
      }
  
      private static URL[] classpathToURLs(String classPath) {
          ArrayList<URL> urls = new ArrayList<>();
          for (String path : classPath.split(File.pathSeparator)) {
              if (path.trim().isEmpty())
                  continue;
              File file = new File(path);
  
              try {
                  if (path.endsWith("/*")) {
                      File parent = new File(new File(file.getCanonicalPath()).getParent());
                      if (parent.isDirectory()) {
                          File[] files = parent.listFiles((dir, name) -> {
                              String lower = name.toLowerCase(Locale.ROOT);
                              return lower.endsWith(".jar") || lower.endsWith(".zip");
                          });
                          if (files != null) {
                              for (File jarFile : files) {
                                  urls.add(jarFile.getCanonicalFile().toURI().toURL());
                              }
                          }
                      }
                  } else if (file.exists()) {
                      urls.add(file.getCanonicalFile().toURI().toURL());
                  }
              } catch (IOException e) {
                  throw new KafkaException(e);
              }
          }
          return urls.toArray(new URL[0]);
      }
  
      @Override
      protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
          synchronized (getClassLoadingLock(name)) {
              Class<?> c = findLoadedClass(name);
  
              if (c == null) {
                  try {
                      c = findClass(name);
                  } catch (ClassNotFoundException e) {
                      // Try parent
                      c = super.loadClass(name, false);
                  }
              }
  
              if (resolve)
                  resolveClass(c);
  
              return c;
          }
      }
  
      @Override
      public URL getResource(String name) {
          URL url = findResource(name);
          if (url == null) {
              // try parent
              url = super.getResource(name);
          }
          return url;
      }
  
      @Override
      public Enumeration<URL> getResources(String name) throws IOException {
          Enumeration<URL> urls1 = findResources(name);
          Enumeration<URL> urls2 = getParent() != null ? getParent().getResources(name) : null;
  
          return new Enumeration<>() {
              @Override
              public boolean hasMoreElements() {
                  return (urls1 != null && urls1.hasMoreElements()) || (urls2 != null && urls2.hasMoreElements());
              }
  
              @Override
              public URL nextElement() {
                  if (urls1 != null && urls1.hasMoreElements())
                      return urls1.nextElement();
                  if (urls2 != null && urls2.hasMoreElements())
                      return urls2.nextElement();
                  throw new NoSuchElementException();
              }
          };
      }
  }
  