/** * Copyright (C) 2011-2015 Bonitasoft S.A. * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation * version 2.1 of the License. * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301, USA. **/ package org.bonitasoft.console.common.server.page; import static org.apache.commons.io.FileUtils.deleteQuietly; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.io.FileUtils; /** * @author Elias Ricken de Medeiros * @author Charles Souillard * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Romain Bioteau * A Classloader adding given custom page resources and bdm resources in its classpath. * This classloader is versioned with a runtime bdm version and should be discard when bdm is updated */ public class CustomPageChildFirstClassLoader extends MonoParentJarFileClassLoader implements VersionedClassloader{ protected Map<String, byte[]> nonJarResources = new HashMap<String, byte[]>(); private boolean isActive = true; private static final Logger LOGGER = Logger.getLogger(CustomPageChildFirstClassLoader.class.getName()); private final CustomPageDependenciesResolver customPageDependenciesResolver; private final BDMClientDependenciesResolver bdmDependenciesResolver; private final String version; CustomPageChildFirstClassLoader(String pageName, CustomPageDependenciesResolver customPageDependenciesResolver, BDMClientDependenciesResolver bdmDependenciesResolver, ClassLoader parent) { super(pageName, new URL[] {}, parent); this.customPageDependenciesResolver = customPageDependenciesResolver; this.bdmDependenciesResolver = bdmDependenciesResolver; this.version = bdmDependenciesResolver.getBusinessDataModelVersion(); } public void addCustomPageResources() { addBDMDependencies(); addOtherDependencies(); } private void addBDMDependencies() { try { addURLs(bdmDependenciesResolver.getBDMDependencies()); } catch (final IOException e) { LOGGER.log(Level.SEVERE, "Failed to add BDM dependencies in ClassLoader", e); } } private void addOtherDependencies() { final Map<String, byte[]> customPageDependencies = customPageDependenciesResolver.resolveCustomPageDependencies(); for (final Map.Entry<String, byte[]> resource : customPageDependencies.entrySet()) { if (resource.getKey().matches(".*\\.jar") && !bdmDependenciesResolver.isABDMDependency(resource.getKey())) { final byte[] data = resource.getValue(); try { final File file = File.createTempFile(resource.getKey(), null, customPageDependenciesResolver.getTempFolder()); file.deleteOnExit(); FileUtils.writeByteArrayToFile(file, data); addURL(new File(file.getAbsolutePath()).toURI().toURL()); } catch (final IOException e) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, String.format("Failed to add file %s in classpath", resource.getKey()), e); } } } else { nonJarResources.put(resource.getKey(), resource.getValue()); } } } @Override public InputStream getResourceAsStream(final String name) { /* * if (!isActive) { * throw new RuntimeException(this.toString() + " is not active anymore. Don't use it."); * } */ InputStream is = getInternalInputstream(name); if (is == null && name.length() > 0 && name.charAt(0) == '/') { is = getInternalInputstream(name.substring(1)); } return is; } private InputStream getInternalInputstream(final String name) { final byte[] classData = loadProcessResource(name); if (classData != null) { return new ByteArrayInputStream(classData); } final InputStream is = super.getResourceAsStream(name); if (is != null) { return is; } return null; } private byte[] loadProcessResource(final String resourceName) { return nonJarResources.containsKey(resourceName) ? nonJarResources.get(resourceName) : new byte[0]; } @Override protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException { /* * if (!isActive) { * throw new RuntimeException(this.toString() + " is not active anymore. Don't use it."); * } */ Class<?> c = null; c = findLoadedClass(name); if (c == null) { try { c = findClass(name); } catch (final ClassNotFoundException e) { // ignore } catch (final LinkageError le) { // might be because of a duplicate loading (concurrency loading), retry to find it one time See BS-2483 c = findLoadedClass(name); if (c == null) { // was not because of duplicate loading: throw the exception throw le; } } } if (c == null) { c = getParent().loadClass(name); } if (resolve) { resolveClass(c); } return c; } public void release() { deleteQuietly(customPageDependenciesResolver.getTempFolder()); isActive = false; } @Override public String toString() { return super.toString() + ", name=" + getName() + ", isActive: " + isActive + ", parent= " + getParent(); } @Override public String getVersion() { return version; } @Override public boolean hasVersion(String version) { return Objects.equals(getVersion(), version); } }