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.names;
018
019import java.io.BufferedReader;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.InputStreamReader;
023import java.util.ArrayList;
024import java.util.List;
025
026import org.apache.commons.discovery.Resource;
027import org.apache.commons.discovery.ResourceDiscover;
028import org.apache.commons.discovery.ResourceIterator;
029import org.apache.commons.discovery.ResourceNameDiscover;
030import org.apache.commons.discovery.ResourceNameIterator;
031import org.apache.commons.discovery.resource.ClassLoaders;
032import org.apache.commons.discovery.resource.DiscoverResources;
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035
036/**
037 * Discover ALL files of a given name, and return resource names
038 * contained within the set of files:
039 * <ul>
040 *   <li>one resource name per line,</li>
041 *   <li>whitespace ignored,</li>
042 *   <li>comments begin with '#'</li>
043 * </ul>
044 *
045 * Default discoverer is DiscoverClassLoaderResources,
046 * but it can be set to any other.
047 */
048public class DiscoverNamesInFile extends ResourceNameDiscoverImpl implements ResourceNameDiscover {
049
050    private static Log log = LogFactory.getLog(DiscoverNamesInFile.class);
051
052    /**
053     * Sets the {@code Log} for this class.
054     *
055     * @param _log This class {@code Log}
056     * @deprecated This method is not thread-safe
057     */
058    @Deprecated
059    public static void setLog(Log _log) {
060        log = _log;
061    }
062
063    private ResourceDiscover _discoverResources;
064
065    private final String _prefix;
066
067    private final String _suffix;
068
069    /**
070     * Construct a new resource discoverer.
071     */
072    public DiscoverNamesInFile() {
073        _discoverResources = new DiscoverResources();
074        _prefix = null;
075        _suffix = null;
076    }
077
078    /**
079     * Construct a new resource discoverer.
080     *
081     * @param prefix The resource name prefix
082     * @param suffix The resource name suffix
083     */
084    public DiscoverNamesInFile(String prefix, String suffix) {
085        _discoverResources = new DiscoverResources();
086        _prefix = prefix;
087        _suffix = suffix;
088    }
089
090    /**
091     * Construct a new resource discoverer.
092     *
093     * @param loaders The class loaders holder
094     */
095    public DiscoverNamesInFile(ClassLoaders loaders) {
096        _discoverResources = new DiscoverResources(loaders);
097        _prefix = null;
098        _suffix = null;
099    }
100
101    /**
102     * Construct a new resource discoverer.
103     *
104     * @param loaders The class loaders holder
105     * @param prefix The resource name prefix
106     * @param suffix The resource name suffix
107     */
108    public DiscoverNamesInFile(ClassLoaders loaders, String prefix, String suffix) {
109        _discoverResources = new DiscoverResources(loaders);
110        _prefix = prefix;
111        _suffix = suffix;
112    }
113
114    /**
115     * Construct a new resource discoverer.
116     *
117     * @param discoverer The discoverer to resolve resources
118     */
119    public DiscoverNamesInFile(ResourceDiscover discoverer) {
120        _discoverResources = discoverer;
121        _prefix = null;
122        _suffix = null;
123    }
124
125    /**
126     * Construct a new resource discoverer.
127     *
128     * @param discoverer The discoverer to resolve resources
129     * @param prefix The resource name prefix
130     * @param suffix The resource name suffix
131     */
132    public DiscoverNamesInFile(ResourceDiscover discoverer, String prefix, String suffix) {
133        _discoverResources = discoverer;
134        _prefix = prefix;
135        _suffix = suffix;
136    }
137
138    /**
139     * Set the discoverer to resolve resources.
140     *
141     * @param discover The discoverer to resolve resources
142     */
143    public void setDiscoverer(ResourceDiscover discover) {
144        _discoverResources = discover;
145    }
146
147    /**
148     * Return the discoverer to resolve resources.
149     *
150     * To be used by downstream elements...
151     *
152     * @return The discoverer to resolve resources
153     */
154    public ResourceDiscover getDiscover() {
155        return _discoverResources;
156    }
157
158    /**
159     * {@inheritDoc}
160     */
161    @Override
162    public ResourceNameIterator findResourceNames(final String serviceName) {
163        String fileName;
164        if (_prefix != null && _prefix.length() > 0) {
165            fileName = _prefix + serviceName;
166        } else {
167            fileName = serviceName;
168        }
169
170        if (_suffix != null && _suffix.length() > 0) {
171            fileName = fileName + _suffix;
172        }
173
174        if (log.isDebugEnabled()) {
175            if (_prefix != null  &&  _suffix != null) {
176                log.debug("find: serviceName='" + serviceName + "' as '" + fileName + "'");
177            } else {
178                log.debug("find: serviceName = '" + fileName + "'");
179            }
180        }
181
182
183        final ResourceIterator files =
184            getDiscover().findResources(fileName);
185
186        return new ResourceNameIterator() {
187
188            private int idx = 0;
189
190            private List<String> classNames = null;
191
192            private String resource = null;
193
194            public boolean hasNext() {
195                if (resource == null) {
196                    resource = getNextClassName();
197                }
198                return resource != null;
199            }
200
201            public String nextResourceName() {
202                String element = resource;
203                resource = null;
204                return element;
205            }
206
207            private String getNextClassName() {
208                if (classNames == null || idx >= classNames.size()) {
209                    classNames = getNextClassNames();
210                    idx = 0;
211                    if (classNames == null) {
212                        return null;
213                    }
214                }
215
216                String className = classNames.get(idx++);
217
218                if (log.isDebugEnabled()) {
219                    log.debug("getNextClassResource: next class='" + className + "'");
220                }
221
222                return className;
223            }
224
225            private List<String> getNextClassNames() {
226                while (files.hasNext()) {
227                    List<String> results = readServices(files.nextResource());
228                    if (results != null  &&  results.size() > 0) {
229                        return results;
230                    }
231                }
232                return null;
233            }
234        };
235    }
236
237    /**
238     * Parses the resource info file and store all the defined SPI implementation classes
239     *
240     * @param info The resource file
241     * @return The list with all SPI implementation names
242     */
243    private List<String> readServices(final Resource info) {
244        List<String> results = new ArrayList<String>();
245
246        InputStream is = info.getResourceAsStream();
247
248        if (is != null) {
249            try {
250                try {
251                    // This code is needed by EBCDIC and other
252                    // strange systems.  It's a fix for bugs
253                    // reported in xerces
254                    BufferedReader rd;
255                    try {
256                        rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
257                    } catch (java.io.UnsupportedEncodingException e) {
258                        rd = new BufferedReader(new InputStreamReader(is));
259                    }
260
261                    try {
262                        String serviceImplName;
263                        while( (serviceImplName = rd.readLine()) != null) {
264                            int idx = serviceImplName.indexOf('#');
265                            if (idx >= 0) {
266                                serviceImplName = serviceImplName.substring(0, idx);
267                            }
268                            serviceImplName = serviceImplName.trim();
269
270                            if (serviceImplName.length() != 0) {
271                                results.add(serviceImplName);
272                            }
273                        }
274                    } finally {
275                        rd.close();
276                    }
277                } finally {
278                    is.close();
279                }
280            } catch (IOException e) {
281                // ignore
282            }
283        }
284
285        return results;
286    }
287
288}