001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.java.bytecode.loader;
021
022 import java.io.Closeable;
023 import java.io.File;
024 import java.io.IOException;
025 import java.net.URL;
026 import java.util.ArrayList;
027 import java.util.Collection;
028 import java.util.Enumeration;
029 import java.util.List;
030
031 import com.google.common.collect.Iterators;
032
033 /**
034 * Class loader, which is able to load classes from a list of JAR files and directories.
035 */
036 public class SquidClassLoader extends ClassLoader implements Closeable {
037
038 private final List<Loader> loaders;
039
040 public SquidClassLoader(Collection<File> files) throws IOException {
041 super(null);
042 loaders = new ArrayList<Loader>();
043 for (File file : files) {
044 if (file.exists()) {
045 if (file.isDirectory()) {
046 loaders.add(new FileSystemLoader(file));
047 } else {
048 loaders.add(new JarLoader(file));
049 }
050 }
051 }
052 }
053
054 @Override
055 protected Class<?> findClass(String name) throws ClassNotFoundException {
056 byte[] classBytes = loadClassBytes(name);
057 if (classBytes == null) {
058 throw new ClassNotFoundException(name);
059 }
060 // TODO Godin: definePackage ?
061 return defineClass(name, classBytes, 0, classBytes.length);
062 }
063
064 private byte[] loadClassBytes(String name) {
065 String resourceName = name.replace('.', '/') + ".class";
066 for (Loader loader : loaders) {
067 byte[] classBytes = loader.loadBytes(resourceName);
068 if (classBytes != null) {
069 return classBytes;
070 }
071 }
072 return null;
073 }
074
075 @Override
076 public URL findResource(String name) {
077 for (Loader loader : loaders) {
078 URL url = loader.findResource(name);
079 if (url != null) {
080 return url;
081 }
082 }
083 return null;
084 }
085
086 @Override
087 protected Enumeration<URL> findResources(String name) throws IOException {
088 List<URL> result = new ArrayList<URL>();
089 for (Loader loader : loaders) {
090 URL url = loader.findResource(name);
091 if (url != null) {
092 result.add(url);
093 }
094 }
095 return Iterators.asEnumeration(result.iterator());
096 }
097
098 /**
099 * Closes this class loader, so that it can no longer be used to load new classes or resources.
100 * Any classes or resources that are already loaded, are still accessible.
101 *
102 * If class loader is already closed, then invoking this method has no effect.
103 */
104 public void close() {
105 for (Loader loader : loaders) {
106 loader.close();
107 }
108 }
109
110 }