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.tools;
018
019import java.util.Enumeration;
020import java.util.NoSuchElementException;
021
022import org.apache.commons.discovery.ResourceClass;
023import org.apache.commons.discovery.ResourceClassIterator;
024import org.apache.commons.discovery.ResourceNameIterator;
025import org.apache.commons.discovery.resource.ClassLoaders;
026import org.apache.commons.discovery.resource.classes.DiscoverClasses;
027import org.apache.commons.discovery.resource.names.DiscoverServiceNames;
028
029/**
030 * [this was ServiceDiscovery12... the 1.1 versus 1.2 issue
031 * has been abstracted to org.apache.commons.discover.jdk.JDKHooks]
032 *
033 * <p>Implement the JDK1.3 'Service Provider' specification.
034 * ( http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html )
035 * </p>
036 *
037 * This class supports any VM, including JDK1.1, via
038 * org.apache.commons.discover.jdk.JDKHooks.
039 *
040 * The caller will first configure the discoverer by adding ( in the desired
041 * order ) all the places to look for the META-INF/services. Currently
042 * we support loaders.
043 *
044 * The findResources() method will check every loader.
045 */
046public class Service {
047
048    /**
049     * Construct a new service discoverer
050     */
051    protected Service() {
052    }
053
054    /**
055     * as described in
056     * sun/jdk1.3.1/docs/guide/jar/jar.html#Service Provider,
057     * Except this uses <code>Enumeration</code>
058     * instead of <code>Interator</code>.
059     *
060     * @param <T> Service Provider Interface type
061     * @param <S> Any type extends the SPI type
062     * @param spiClass Service Provider Interface Class
063     * @return Enumeration of class instances ({@code S})
064     */
065    public static <T, S extends T> Enumeration<S> providers(Class<T> spiClass) {
066        return providers(new SPInterface<T>(spiClass), null);
067    }
068
069    /**
070     * This version lets you specify constructor arguments..
071     *
072     * @param <T> Service Provider Interface type
073     * @param <S> Any type extends the SPI type
074     * @param spi SPI to look for and load.
075     * @param loaders loaders to use in search.
076     *        If <code>null</code> then use ClassLoaders.getAppLoaders().
077     * @return Enumeration of class instances ({@code S})
078     */
079    public static <T, S extends T> Enumeration<S> providers(final SPInterface<T> spi,
080                                                            ClassLoaders loaders) {
081        if (loaders == null) {
082            loaders = ClassLoaders.getAppLoaders(spi.getSPClass(),
083                                                 Service.class,
084                                                 true);
085        }
086
087        ResourceNameIterator servicesIter =
088            (new DiscoverServiceNames(loaders)).findResourceNames(spi.getSPName());
089
090        final ResourceClassIterator<T> services =
091            (new DiscoverClasses<T>(loaders)).findResourceClasses(servicesIter);
092
093        return new Enumeration<S>() {
094
095            private S object = getNextClassInstance();
096
097            public boolean hasMoreElements() {
098                return object != null;
099            }
100
101            public S nextElement() {
102                if (object == null) {
103                    throw new NoSuchElementException();
104                }
105
106                S obj = object;
107                object = getNextClassInstance();
108                return obj;
109            }
110
111            private S getNextClassInstance() {
112                while (services.hasNext()) {
113                    ResourceClass<S> info = services.nextResourceClass();
114                    try {
115                        return spi.newInstance(info.loadClass());
116                    } catch (Exception e) {
117                        // ignore
118                    } catch (LinkageError le) {
119                        // ignore
120                    }
121                }
122                return null;
123            }
124        };
125    }
126
127}