001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.discovery.resource.classes;
018
019import java.net.URL;
020import java.security.CodeSource;
021import java.util.HashSet;
022import java.util.Set;
023
024import org.apache.commons.discovery.ResourceClass;
025import org.apache.commons.discovery.ResourceClassDiscover;
026import org.apache.commons.discovery.ResourceClassIterator;
027import org.apache.commons.discovery.resource.ClassLoaders;
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030
031/**
032 * The findResources() method will check every loader.
033 *
034 * @param <T> The SPI type
035 */
036public class DiscoverClasses<T> extends ResourceClassDiscoverImpl<T> implements ResourceClassDiscover<T> {
037
038    private static Log log = LogFactory.getLog(DiscoverClasses.class);
039
040    /**
041     * Sets the {@code Log} for this class.
042     *
043     * @param _log This class {@code Log}
044     * @deprecated This method is not thread-safe
045     */
046    @Deprecated
047    public static void setLog(Log _log) {
048        log = _log;
049    }
050
051    /**
052     * Construct a new resource discoverer
053     */
054    public DiscoverClasses() {
055        super();
056    }
057
058    /**
059     * Construct a new resource discoverer.
060     *
061     * @param classLoaders The class loaders holder
062     */
063    public DiscoverClasses(ClassLoaders classLoaders) {
064        super(classLoaders);
065    }
066
067    /**
068     * {@inheritDoc}
069     */
070    @Override
071    public ResourceClassIterator<T> findResourceClasses(final String className) {
072        final String resourceName = className.replace('.','/') + ".class";
073
074        if (log.isDebugEnabled()) {
075            log.debug("find: className='" + className + "'");
076        }
077
078        return new ResourceClassIterator<T>() {
079
080            private final Set<URL> history = new HashSet<URL>();
081
082            private int idx = 0;
083
084            private ResourceClass<T> resource = null;
085
086            public boolean hasNext() {
087                if (resource == null) {
088                    resource = getNextClass();
089                }
090                return resource != null;
091            }
092
093            @Override
094            public ResourceClass<T> nextResourceClass() {
095                ResourceClass<T> element = resource;
096                resource = null;
097                return element;
098            }
099
100            private ResourceClass<T> getNextClass() {
101                while (idx < getClassLoaders().size()) {
102                    ClassLoader loader = getClassLoaders().get(idx++);
103
104                    URL url = null;
105
106                    try {
107                        url = loader.getResource(resourceName);
108                    } catch (UnsupportedOperationException e) {
109                        // ignore
110                    }
111
112                    if (url == null) {
113                        try {
114                            CodeSource codeSource = loader.loadClass(className)
115                                .getProtectionDomain()
116                                .getCodeSource();
117                            if (codeSource != null) {
118                                url = new URL(codeSource.getLocation(), resourceName);
119                            }
120                            // else keep url null
121                        } catch (Exception le) {
122                            // keep url null
123                        }
124                    }
125
126                    if (url != null) {
127                        if (history.add(url)) {
128                            if (log.isDebugEnabled()) {
129                                log.debug("getNextClass: next URL='" + url + "'");
130                            }
131
132                            return new ResourceClass<T>(className, url, loader);
133                        }
134                        if (log.isDebugEnabled()) {
135                            log.debug("getNextClass: duplicate URL='" + url + "'");
136                        }
137                    } else {
138                        if (log.isDebugEnabled()) {
139                            log.debug("getNextClass: loader " + loader + ": '" + resourceName + "' not found");
140                        }
141                    }
142                }
143                return null;
144            }
145        };
146    }
147
148}