/*
 * 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.geode.modules.session.installer;

import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Classloader, which allows finding classes in jars within jars. This is used to check whether a
 * listener, as found in web.xml, is a ServletContextListener
 */
@SuppressWarnings("deprecation")
public class JarClassLoader extends URLClassLoader {

  public JarClassLoader(URL[] urls, ClassLoader parent) {
    super(urls, parent);

    try {
      for (URL url : urls) {
        if (isJar(url.getFile())) {
          addJarResource(new File(url.getPath()));
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  private void addJarResource(File file) throws IOException {
    try (JarFile jarFile = new JarFile(file)) {
      addURL(file.toURL());
      Enumeration<JarEntry> jarEntries = jarFile.entries();
      while (jarEntries.hasMoreElements()) {
        JarEntry jarEntry = jarEntries.nextElement();
        if (!jarEntry.isDirectory() && isJar(jarEntry.getName())) {
          addJarResource(jarEntryAsFile(jarFile, jarEntry));
        }
      }
    }
  }

  @Override
  protected synchronized Class<?> loadClass(String name, boolean resolve)
      throws ClassNotFoundException {
    try {
      Class<?> clazz = findLoadedClass(name);
      if (clazz == null) {
        clazz = findClass(name);
        if (resolve) {
          resolveClass(clazz);
        }
      }
      return clazz;
    } catch (ClassNotFoundException e) {
      return super.loadClass(name, resolve);
    }
  }

  private static void close(Closeable closeable) {
    if (closeable != null) {
      try {
        closeable.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  private static boolean isJar(String fileName) {
    return fileName != null && (fileName.toLowerCase().endsWith(".jar")
        || fileName.toLowerCase().endsWith(".war") || fileName.toLowerCase().endsWith(".ear"));
  }

  private static File jarEntryAsFile(JarFile jarFile, JarEntry jarEntry) throws IOException {
    InputStream input = null;
    OutputStream output = null;
    try {
      String name = jarEntry.getName().replace('/', '_');
      int i = name.lastIndexOf(".");
      String extension = i > -1 ? name.substring(i) : "";
      File file = File.createTempFile(name.substring(0, name.length() - extension.length()) + ".",
          extension);
      file.deleteOnExit();
      input = jarFile.getInputStream(jarEntry);
      output = new FileOutputStream(file);
      int readCount;
      byte[] buffer = new byte[4096];
      while ((readCount = input.read(buffer)) != -1) {
        output.write(buffer, 0, readCount);
      }
      return file;
    } finally {
      close(input);
      close(output);
    }
  }

}
