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 backtype.storm.utils;
  
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.URL;
  import java.net.URLClassLoader;
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import sun.misc.CompoundEnumeration;
  
  
  public class WorkerClassLoader extends URLClassLoader {
      private static final Logger LOG = LoggerFactory.getLogger(WorkerClassLoader.class);
  
      private ClassLoader defaultClassLoader;
      private ClassLoader JDKClassLoader;
  
      private boolean isDebug;
  
      protected static WorkerClassLoader instance;
  
      protected static boolean enable;
  
      protected static Map<Thread, ClassLoader> threadContextCache;
  
      protected WorkerClassLoader(URL[] urls, ClassLoader defaultClassLoader, ClassLoader JDKClassLoader, boolean isDebug) {
          super(urls, JDKClassLoader);
          this.defaultClassLoader = defaultClassLoader;
          this.JDKClassLoader = JDKClassLoader;
          this.isDebug = isDebug;
      }
  
      // for all log go through logback when enable classloader
      protected boolean isLogByDefault(String name) {
          return name.startsWith("org.apache.log4j") || name.startsWith("org.slf4j");
      }
  
      protected boolean isLoadByDefault(String name) {
          return name.startsWith("backtype.storm") || name.startsWith("com.alibaba.jstorm") || isLogByDefault(name);
      }
  
      @Override
      public Class<?> loadClass(String name) throws ClassNotFoundException {
          Class<?> result = null;
          try {
              result = this.findLoadedClass(name);
              if (result != null) {
                  return result;
              }
  
              try {
                  result = JDKClassLoader.loadClass(name);
                  if (result != null)
                      return result;
              } catch (Exception ignored) {
              }
  
              try {
                  if (!isLoadByDefault(name)) {
                      result = findClass(name);
                      if (result != null) {
                          return result;
                      }
                  }
              } catch (Exception ignored) {
              }
  
              result = defaultClassLoader.loadClass(name);
              return result;
          } finally {
              if (result != null) {
                  ClassLoader resultClassLoader = result.getClassLoader();
                  LOG.info("Successfully load class " + name + " by " + resultClassLoader + ",threadContextLoader:"
                          + Thread.currentThread().getContextClassLoader());
              } else {
                  LOG.warn("Failed to load class " + name + ",threadContextLoader:" + Thread.currentThread().getContextClassLoader());
              }
  
              if (isDebug) {
                  LOG.info(Utils.printStack());
              }
          }
  
      }
  
      public static WorkerClassLoader mkInstance(URL[] urls, ClassLoader DefaultClassLoader, ClassLoader JDKClassLoader,
                                                 boolean enable, boolean isDebug) {
          WorkerClassLoader.enable = enable;
          if (!enable) {
              LOG.info("Don't enable UserDefine ClassLoader");
              return null;
          }
  
          synchronized (WorkerClassLoader.class) {
              if (instance == null) {
                  instance = new WorkerClassLoader(urls, DefaultClassLoader, JDKClassLoader, isDebug);
                  threadContextCache = new ConcurrentHashMap<>();
              }
          }
  
          LOG.info("Successfully created classloader " + mk_list(urls));
          return instance;
      }
  
      public static WorkerClassLoader getInstance() {
          return instance;
      }
  
      public static boolean isEnable() {
          return enable;
      }
  
      public static void switchThreadContext() {
          if (!enable) {
              return;
          }
  
          Thread thread = Thread.currentThread();
          ClassLoader oldClassLoader = thread.getContextClassLoader();
          threadContextCache.put(thread, oldClassLoader);
          thread.setContextClassLoader(instance);
      }
  
      public static void restoreThreadContext() {
          if (!enable) {
              return;
          }
  
          Thread thread = Thread.currentThread();
          ClassLoader oldClassLoader = threadContextCache.get(thread);
          if (oldClassLoader != null) {
              thread.setContextClassLoader(oldClassLoader);
          } else {
              LOG.info("No context classloader of " + thread.getName());
          }
      }
  
      private static <V> List<V> mk_list(V... args) {
          ArrayList<V> rtn = new ArrayList<V>();
          for (V o : args) {
              rtn.add(o);
          }
          return rtn;
      }
  
      public Enumeration<URL> getResources(String name) throws IOException {
          Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
          tmp[0] = super.getResources(name);
          tmp[1] = defaultClassLoader.getResources(name);
          return new CompoundEnumeration<>(tmp);
      }
  
      public InputStream getResourceAsStream(String name) {
          InputStream is = super.getResourceAsStream(name);
          if (is == null) {
              is = defaultClassLoader.getResourceAsStream(name);
          }
          return is;
      }
  }
  