001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2009 SonarSource SA
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;
021    
022    import org.codehaus.classworlds.ClassRealm;
023    import org.codehaus.classworlds.ClassWorld;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    
027    import java.io.File;
028    import java.net.MalformedURLException;
029    import java.net.URL;
030    import java.util.Arrays;
031    import java.util.Collection;
032    
033    public final class ClassworldsClassLoader {
034      private static final Logger LOG = LoggerFactory.getLogger(ClassworldsClassLoader.class);
035    
036      private ClassworldsClassLoader() {
037        // only static methods
038      }
039      
040      public static ClassLoader create(File bytecodeFileOrDirectory) {
041        return create(Arrays.asList(bytecodeFileOrDirectory));
042      }
043      
044      public static ClassLoader create(Collection<File> bytecodeFilesOrDirectories) {
045        try {
046          ClassWorld world = new ClassWorld();
047          ClassRealm realm = world.newRealm("squid.project");
048    
049          for (File bytecode : bytecodeFilesOrDirectories) {
050            URL url = getURL(bytecode);
051            if (bytecode.isFile() && url.toString().endsWith(".class")) {
052              // CLASS file is not supported by ClassWorlds
053              //TODO log warning
054    
055            } else {
056              // JAR file or directory
057              realm.addConstituent(url);
058            }
059          }
060    
061          if (LOG.isDebugEnabled()) {
062            LOG.debug("----- Classpath analyzed by Squid:");
063            for (URL url : realm.getConstituents()) {
064              LOG.debug(url.toString());
065            }
066            LOG.debug("-----");
067          }
068    
069          return realm.getClassLoader();
070    
071        } catch (Exception e) {
072          throw new IllegalStateException("Can not create classloader", e);
073        }
074      }
075    
076      private static URL getURL(File file) throws MalformedURLException {
077        URL url = file.toURI().toURL();
078        if (file.isDirectory() && !url.toString().endsWith("/")) {
079          /*
080           See ClassRealm javadoc :
081           If the constituent is a directory, then the URL must end with a slash (/). Otherwise the constituent will be treated as a JAR file.
082          */
083          url = new URL(url.toString() + "/");
084        }
085        return url;
086      }
087    }