/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed 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 com.hazelcast.util; import com.hazelcast.internal.usercodedeployment.impl.ClassloadingMutexProvider; import com.hazelcast.spi.annotation.PrivateApi; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.net.URL; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.regex.Pattern; import static com.hazelcast.nio.IOUtil.closeResource; import static com.hazelcast.util.Preconditions.isNotNull; /** * This is used to separate Server and Client inside the same JVM on new standalone client unit tests. * * NEVER EVER use this anywhere in production! :D */ @PrivateApi public class FilteringClassLoader extends ClassLoader { private static final int BUFFER_SIZE = 1024; private final ClassloadingMutexProvider mutexProvider = new ClassloadingMutexProvider(); private final Pattern pattern = Pattern.compile("\\."); private final byte[] buffer = new byte[BUFFER_SIZE]; private final List<String> excludePackages; private final ClassLoader delegatingClassLoader; private final String enforcedSelfLoadingPackage; public FilteringClassLoader(List<String> excludePackages, String enforcedSelfLoadingPackage) { this.excludePackages = Collections.unmodifiableList(excludePackages); this.enforcedSelfLoadingPackage = enforcedSelfLoadingPackage; try { Field parent = ClassLoader.class.getDeclaredField("parent"); parent.setAccessible(true); delegatingClassLoader = (ClassLoader) parent.get(this); parent.set(this, null); } catch (Exception e) { throw new RuntimeException(e); } } @Override public URL getResource(String name) { return delegatingClassLoader.getResource(name); } @Override public Enumeration<URL> getResources(String name) throws IOException { return delegatingClassLoader.getResources(name); } @Override public InputStream getResourceAsStream(String name) { return delegatingClassLoader.getResourceAsStream(name); } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { isNotNull(name, "name"); for (String excludePackage : excludePackages) { if (name.startsWith(excludePackage)) { throw new ClassNotFoundException(name + " - Package excluded explicitly!"); } } if (enforcedSelfLoadingPackage != null && name.startsWith(enforcedSelfLoadingPackage)) { Closeable classLoadingMutex = mutexProvider.getMutexForClass(name); try { //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (classLoadingMutex) { Class<?> clazz = findLoadedClass(name); if (clazz == null) { clazz = loadAndDefineClass(name); } return clazz; } } finally { closeResource(classLoadingMutex); } } return delegatingClassLoader.loadClass(name); } private Class<?> loadAndDefineClass(String name) throws ClassNotFoundException { InputStream is = null; ByteArrayOutputStream os = null; try { is = getResourceAsStream(pattern.matcher(name).replaceAll("/").concat(".class")); os = new ByteArrayOutputStream(); int length; while ((length = is.read(buffer)) != -1) { os.write(buffer, 0, length); } byte[] data = os.toByteArray(); return defineClass(name, data, 0, data.length); } catch (Exception e) { throw new ClassNotFoundException(name, e); } finally { closeResource(os); closeResource(is); } } }