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 }