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.resource.names;
018    
019    import java.io.BufferedReader;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.InputStreamReader;
023    import java.util.ArrayList;
024    import java.util.List;
025    
026    import org.apache.commons.discovery.Resource;
027    import org.apache.commons.discovery.ResourceDiscover;
028    import org.apache.commons.discovery.ResourceIterator;
029    import org.apache.commons.discovery.ResourceNameDiscover;
030    import org.apache.commons.discovery.ResourceNameIterator;
031    import org.apache.commons.discovery.resource.ClassLoaders;
032    import org.apache.commons.discovery.resource.DiscoverResources;
033    import org.apache.commons.logging.Log;
034    import 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     */
048    public 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    }