blob: 3ef247627a4d851e42b7f1008df1f4230ffcf69a [file] [log] [blame]
/*
* 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.ignite.testframework;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.CodeSource;
import java.security.SecureClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
/**
* Jar class loader.
*/
public final class GridJarClassLoader extends SecureClassLoader {
/** Cached loaded classes as bytes. */
private final Map<String, byte[]> clsArrs;
/** List of excluded classes/packages. */
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
private List<String> excludedCls = new ArrayList<>();
/** */
private static GridJarClassLoader instance;
/**
* Get classloader singleton instance.
*
* @param files Files.
* @param parent Parent classloader.
* @return Instance of Jar class loader.
* @throws IOException If fies can't be read,
*/
public static synchronized GridJarClassLoader getInstance(List<String> files, ClassLoader parent)
throws IOException {
if (instance == null)
instance = new GridJarClassLoader(files, parent);
return instance;
}
/**
* Constructor.
*
* @param files Files.
* @param parent Parent classloader.
* @throws IOException If fies can't be read,
*/
private GridJarClassLoader(Iterable<String> files, ClassLoader parent) throws IOException {
super(parent);
clsArrs = new HashMap<>();
for (String fileName: files)
readJarFile(fileName);
}
/** {@inheritDoc} */
@Override protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
boolean excluded = false;
for (String cls: excludedCls)
if (name.startsWith(cls)) {
excluded = true;
break;
}
// If class is from Jar file(s) and not in excluded (note we use name with '.').
if (clsArrs.containsKey(name) && !excluded) {
Class<?> cls = findLoadedClass(name);
if (cls == null)
cls = findClass(name);
if (resolve)
resolveClass(cls);
return cls;
}
return super.loadClass(name, resolve);
}
/** {@inheritDoc} */
@Override protected Class<?> findClass(String name) throws ClassNotFoundException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
int i = name.lastIndexOf('.');
if (i >= 0)
sm.checkPackageDefinition(name.substring(0, i));
}
byte[] buf = clsArrs.get(name);
if (buf != null)
return defineClass(name, buf, 0, buf.length, (CodeSource)null);
throw new ClassNotFoundException(name);
}
/**
* Reads JAR file and stored classes locally.
*
* @param fileName Name of file to read.
* @throws IOException If read failed.
*/
private void readJarFile(String fileName) throws IOException {
JarEntry je;
JarInputStream jis = new JarInputStream(new FileInputStream(fileName));
while ((je = jis.getNextJarEntry()) != null) {
String jarName = je.getName();
if (jarName.endsWith(".class"))
loadClassBytes(jis, jarName);
// Else ignore it; it could be an image or audio file.
jis.closeEntry();
}
}
/**
* Loads class bytes to storege.
*
* @param jis Input stream.
* @param jarName Name of the JAR file.
* @throws IOException If read failed.
*/
private void loadClassBytes(JarInputStream jis, String jarName) throws IOException {
BufferedInputStream jarBuf = new BufferedInputStream(jis);
ByteArrayOutputStream jarOut = new ByteArrayOutputStream();
int b;
while ((b = jarBuf.read()) != -1)
jarOut.write(b);
// Remove ".class".
String urlName = jarName.substring(0, jarName.length() - 6);
String name = urlName.replace('/', '.');
clsArrs.put(name, jarOut.toByteArray());
}
/**
* @return the excludedCls
*/
public List<String> getExcludedCls() {
return excludedCls;
}
/**
* @param excludedCls the excludedCls to set
*/
public void setExcludedCls(List<String> excludedCls) {
this.excludedCls = excludedCls;
}
}