001    package com.thaiopensource.util;
002    
003    import java.util.Enumeration;
004    import java.util.NoSuchElementException;
005    import java.util.Vector;
006    import java.io.Reader;
007    import java.io.InputStream;
008    import java.io.InputStreamReader;
009    import java.io.BufferedReader;
010    import java.io.IOException;
011    import java.io.UnsupportedEncodingException;
012    import java.net.URL;
013    
014    public class Service {
015      private final Class serviceClass;
016      private final Enumeration configFiles;
017      private Enumeration classNames = null;
018      private final Vector providers = new Vector();
019      private Loader loader;
020    
021      private class ProviderEnumeration implements Enumeration {
022        private int nextIndex = 0;
023    
024        public boolean hasMoreElements() {
025          return nextIndex < providers.size() || moreProviders();
026        }
027    
028        public Object nextElement() {
029          try {
030            return providers.elementAt(nextIndex++);
031          }
032          catch (ArrayIndexOutOfBoundsException e) {
033            throw new NoSuchElementException();
034          }
035        }
036      }
037    
038      private static class Singleton implements Enumeration {
039        private Object obj;
040        private Singleton(Object obj) {
041          this.obj = obj;
042        }
043    
044        public boolean hasMoreElements() {
045          return obj != null;
046        }
047    
048        public Object nextElement() {
049          if (obj == null)
050            throw new NoSuchElementException();
051          Object tem = obj;
052          obj = null;
053          return tem;
054        }
055      }
056    
057      // JDK 1.1
058      private static class Loader {
059        Enumeration getResources(String resName) {
060          ClassLoader cl = Loader.class.getClassLoader();
061          URL url;
062          if (cl == null)
063            url = ClassLoader.getSystemResource(resName);
064          else
065            url = cl.getResource(resName);
066          return new Singleton(url);
067        }
068    
069        Class loadClass(String name) throws ClassNotFoundException {
070          return Class.forName(name);
071        }
072      }
073    
074      // JDK 1.2+
075      private static class Loader2 extends Loader {
076        private ClassLoader cl;
077    
078        Loader2() {
079          cl = Loader2.class.getClassLoader();
080          // If the thread context class loader has the class loader
081          // of this class as an ancestor, use the thread context class
082          // loader.  Otherwise, the thread context class loader
083          // probably hasn't been set up properly, so don't use it.
084          ClassLoader clt = Thread.currentThread().getContextClassLoader();
085          for (ClassLoader tem = clt; tem != null; tem = tem.getParent())
086            if (tem == cl) {
087              cl = clt;
088              break;
089            }
090        }
091    
092        Enumeration getResources(String resName) {
093          try {
094            Enumeration resources = cl.getResources(resName);
095            if (resources.hasMoreElements())
096              return resources;
097            // Some application servers apparently do not implement findResources
098            // in their class loaders, so fall back to getResource.
099            return new Singleton(cl.getResource(resName));
100          }
101          catch (IOException e) {
102            return new Singleton(null);
103          }
104        }
105    
106        Class loadClass(String name) throws ClassNotFoundException {
107          return Class.forName(name, true, cl);
108        }
109      }
110    
111      public Service(Class cls) {
112        try {
113          loader = new Loader2();
114        }
115        catch (NoSuchMethodError e) {
116          loader = new Loader();
117        }
118        serviceClass = cls;
119        String resName = "META-INF/services/" + serviceClass.getName();
120        configFiles = loader.getResources(resName);
121      }
122    
123      public Enumeration getProviders() {
124        return new ProviderEnumeration();
125      }
126    
127      synchronized private boolean moreProviders() {
128        for (;;) {
129          while (classNames == null) {
130            if (!configFiles.hasMoreElements())
131              return false;
132            classNames = parseConfigFile((URL)configFiles.nextElement());
133          }
134          while (classNames.hasMoreElements()) {
135            String className = (String)classNames.nextElement();
136            try {
137              Class cls = loader.loadClass(className);
138              Object obj = cls.newInstance();
139              if (serviceClass.isInstance(obj)) {
140                providers.addElement(obj);
141                return true;
142              }
143            }
144            catch (ClassNotFoundException e) { }
145            catch (InstantiationException e) { }
146            catch (IllegalAccessException e) { }
147            catch (LinkageError e) { }
148          }
149          classNames = null;
150        }
151      }
152    
153      private static final int START = 0;
154      private static final int IN_NAME = 1;
155      private static final int IN_COMMENT = 2;
156    
157      private static Enumeration parseConfigFile(URL url) {
158        try {
159          InputStream in = url.openStream();
160          Reader r;
161          try {
162            r = new InputStreamReader(in, "UTF-8");
163          }
164          catch (UnsupportedEncodingException e) {
165            r = new InputStreamReader(in, "UTF8");
166          }
167          r = new BufferedReader(r);
168          Vector tokens = new Vector();
169          StringBuffer tokenBuf = new StringBuffer();
170          int state = START;
171          for (;;) {
172            int n = r.read();
173            if (n < 0)
174              break;
175            char c = (char)n;
176            switch (c) {
177            case '\r':
178            case '\n':
179              state = START;
180              break;
181            case ' ':
182            case '\t':
183              break;
184            case '#':
185              state = IN_COMMENT;
186              break;
187            default:
188              if (state != IN_COMMENT) {
189                state = IN_NAME;
190                tokenBuf.append(c);
191              }
192              break;
193            }
194            if (tokenBuf.length() != 0 && state != IN_NAME) {
195              tokens.addElement(tokenBuf.toString());
196              tokenBuf.setLength(0);
197            }
198          }
199          if (tokenBuf.length() != 0)
200            tokens.addElement(tokenBuf.toString());
201          return tokens.elements();
202        }
203        catch (IOException e) {
204          return null;
205        }
206      }
207    
208      public static void main(String[] args) throws ClassNotFoundException {
209        Service svc = new Service(Class.forName(args[0]));
210        for (Enumeration e = svc.getProviders(); e.hasMoreElements();)
211          System.out.println(e.nextElement().getClass().getName());
212      }
213    }