001/* 002 * #%L 003 * Netarchivesuite - common 004 * %% 005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023package dk.netarkivet.common.utils.batch; 024 025import java.io.ByteArrayOutputStream; 026import java.io.File; 027import java.io.IOException; 028import java.io.InputStream; 029import java.io.Serializable; 030import java.util.Enumeration; 031import java.util.HashMap; 032import java.util.Map; 033import java.util.jar.JarEntry; 034import java.util.jar.JarFile; 035 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039import dk.netarkivet.common.exceptions.ArgumentNotValid; 040import dk.netarkivet.common.exceptions.IOFailure; 041import dk.netarkivet.common.utils.StreamUtils; 042 043/** 044 * ByteJarLoader is a ClassLoader that stores java classes in a map where the key to the map is the class name, and the 045 * value is the class stored as a byte array. 046 */ 047@SuppressWarnings("serial") 048public class ByteJarLoader extends ClassLoader implements Serializable { 049 050 /** The log. */ 051 private static final transient Logger log = LoggerFactory.getLogger(ByteJarLoader.class); 052 053 /** The map, that holds the class data. */ 054 Map<String, byte[]> binaryData = new HashMap<String, byte[]>(); 055 056 /** Java package separator. */ 057 private static final String JAVA_PACKAGE_SEPARATOR = "."; 058 059 /** Directory separator. */ 060 private static final String DIRECTOR_SEPARATOR = "/"; 061 062 /** 063 * Constructor for the ByteLoader. 064 * 065 * @param files An array of files, which are assumed to be jar-files, but they need not have the extension .jar 066 */ 067 public ByteJarLoader(File... files) { 068 ArgumentNotValid.checkNotNull(files, "File ... files"); 069 ArgumentNotValid.checkTrue(files.length != 0, "Should not be empty array"); 070 for (File file : files) { 071 try { 072 JarFile jarFile = new JarFile(file); 073 for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements();) { 074 JarEntry entry = e.nextElement(); 075 String name = entry.getName(); 076 InputStream in = jarFile.getInputStream(entry); 077 ByteArrayOutputStream out = new ByteArrayOutputStream((int) entry.getSize()); 078 StreamUtils.copyInputStreamToOutputStream(in, out); 079 log.trace("Entering data for class '{}'", name); 080 binaryData.put(name, out.toByteArray()); 081 } 082 } catch (IOException e) { 083 throw new IOFailure("Failed to load jar file '" + file.getAbsolutePath() + "': " + e); 084 } 085 } 086 } 087 088 /** 089 * Lookup and return the Class with the given className. This method overrides the ClassLoader.findClass method. 090 * 091 * @param className The name of the class to lookup 092 * @return the Class with the given className. 093 * @throws ClassNotFoundException If the class could not be found 094 */ 095 @SuppressWarnings({"rawtypes", "unchecked"}) 096 public Class findClass(String className) throws ClassNotFoundException { 097 ArgumentNotValid.checkNotNullOrEmpty(className, "String className"); 098 // replace all dots with '/' in the className before looking it up 099 // in the 100 // hashmap 101 // Note: The class is stored in the hashmap with a .class extension 102 String realClassName = className.replace(JAVA_PACKAGE_SEPARATOR, DIRECTOR_SEPARATOR) + ".class"; 103 104 if (binaryData.isEmpty()) { 105 log.warn("No data loaded for class with name '{}'", className); 106 } 107 if (binaryData.containsKey(realClassName)) { 108 final byte[] bytes = binaryData.get(realClassName); 109 return defineClass(className, bytes, 0, bytes.length); 110 } else { 111 return super.findClass(className); 112 } 113 } 114 115}