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 }