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     */
017    package org.apache.commons.discovery.tools;
018    
019    import java.util.Enumeration;
020    import java.util.NoSuchElementException;
021    
022    import org.apache.commons.discovery.ResourceClass;
023    import org.apache.commons.discovery.ResourceClassIterator;
024    import org.apache.commons.discovery.ResourceNameIterator;
025    import org.apache.commons.discovery.resource.ClassLoaders;
026    import org.apache.commons.discovery.resource.classes.DiscoverClasses;
027    import 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     */
046    public 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    }