001 /** 002 * Copyright (c) 2001, Thai Open Source Software Center Ltd 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without 006 * modification, are permitted provided that the following conditions are 007 * met: 008 * 009 * Redistributions of source code must retain the above copyright 010 * notice, this list of conditions and the following disclaimer. 011 * 012 * Redistributions in binary form must reproduce the above copyright 013 * notice, this list of conditions and the following disclaimer in 014 * the documentation and/or other materials provided with the 015 * distribution. 016 * 017 * Neither the name of the Thai Open Source Software Center Ltd nor 018 * the names of its contributors may be used to endorse or promote 019 * products derived from this software without specific prior written 020 * permission. 021 * 022 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 023 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 024 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 025 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 026 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 027 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 028 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 029 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 030 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 031 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 032 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 033 */ 034 package org.relaxng.datatype.helpers; 035 036 import org.relaxng.datatype.DatatypeLibraryFactory; 037 import org.relaxng.datatype.DatatypeLibrary; 038 import java.util.Enumeration; 039 import java.util.NoSuchElementException; 040 import java.util.Vector; 041 import java.io.Reader; 042 import java.io.InputStream; 043 import java.io.InputStreamReader; 044 import java.io.BufferedReader; 045 import java.io.IOException; 046 import java.io.UnsupportedEncodingException; 047 import java.net.URL; 048 049 /** 050 * Discovers the datatype library implementation from the classpath. 051 * 052 * <p> 053 * The call of the createDatatypeLibrary method finds an implementation 054 * from a given datatype library URI at run-time. 055 */ 056 public class DatatypeLibraryLoader implements DatatypeLibraryFactory { 057 private final Service service = new Service(DatatypeLibraryFactory.class); 058 059 public DatatypeLibrary createDatatypeLibrary(String uri) { 060 for (Enumeration e = service.getProviders(); 061 e.hasMoreElements();) { 062 DatatypeLibraryFactory factory 063 = (DatatypeLibraryFactory)e.nextElement(); 064 DatatypeLibrary library = factory.createDatatypeLibrary(uri); 065 if (library != null) 066 return library; 067 } 068 return null; 069 } 070 071 private static class Service { 072 private final Class serviceClass; 073 private final Enumeration configFiles; 074 private Enumeration classNames = null; 075 private final Vector providers = new Vector(); 076 private Loader loader; 077 078 private class ProviderEnumeration implements Enumeration { 079 private int nextIndex = 0; 080 081 public boolean hasMoreElements() { 082 return nextIndex < providers.size() || moreProviders(); 083 } 084 085 public Object nextElement() { 086 try { 087 return providers.elementAt(nextIndex++); 088 } 089 catch (ArrayIndexOutOfBoundsException e) { 090 throw new NoSuchElementException(); 091 } 092 } 093 } 094 095 private static class Singleton implements Enumeration { 096 private Object obj; 097 private Singleton(Object obj) { 098 this.obj = obj; 099 } 100 101 public boolean hasMoreElements() { 102 return obj != null; 103 } 104 105 public Object nextElement() { 106 if (obj == null) 107 throw new NoSuchElementException(); 108 Object tem = obj; 109 obj = null; 110 return tem; 111 } 112 } 113 114 // JDK 1.1 115 private static class Loader { 116 Enumeration getResources(String resName) { 117 ClassLoader cl = Loader.class.getClassLoader(); 118 URL url; 119 if (cl == null) 120 url = ClassLoader.getSystemResource(resName); 121 else 122 url = cl.getResource(resName); 123 return new Singleton(url); 124 } 125 126 Class loadClass(String name) throws ClassNotFoundException { 127 return Class.forName(name); 128 } 129 } 130 131 // JDK 1.2+ 132 private static class Loader2 extends Loader { 133 private ClassLoader cl; 134 135 Loader2() { 136 cl = Loader2.class.getClassLoader(); 137 // If the thread context class loader has the class loader 138 // of this class as an ancestor, use the thread context class 139 // loader. Otherwise, the thread context class loader 140 // probably hasn't been set up properly, so don't use it. 141 ClassLoader clt = Thread.currentThread().getContextClassLoader(); 142 for (ClassLoader tem = clt; tem != null; tem = tem.getParent()) 143 if (tem == cl) { 144 cl = clt; 145 break; 146 } 147 } 148 149 Enumeration getResources(String resName) { 150 try { 151 Enumeration resources = cl.getResources(resName); 152 if (resources.hasMoreElements()) 153 return resources; 154 // Some application servers apparently do not implement findResources 155 // in their class loaders, so fall back to getResource. 156 return new Singleton(cl.getResource(resName)); 157 } 158 catch (IOException e) { 159 return new Singleton(null); 160 } 161 } 162 163 Class loadClass(String name) throws ClassNotFoundException { 164 return Class.forName(name, true, cl); 165 } 166 } 167 168 public Service(Class cls) { 169 try { 170 loader = new Loader2(); 171 } 172 catch (NoSuchMethodError e) { 173 loader = new Loader(); 174 } 175 serviceClass = cls; 176 String resName = "META-INF/services/" + serviceClass.getName(); 177 configFiles = loader.getResources(resName); 178 } 179 180 public Enumeration getProviders() { 181 return new ProviderEnumeration(); 182 } 183 184 synchronized private boolean moreProviders() { 185 for (;;) { 186 while (classNames == null) { 187 if (!configFiles.hasMoreElements()) 188 return false; 189 classNames = parseConfigFile((URL)configFiles.nextElement()); 190 } 191 while (classNames.hasMoreElements()) { 192 String className = (String)classNames.nextElement(); 193 try { 194 Class cls = loader.loadClass(className); 195 Object obj = cls.newInstance(); 196 if (serviceClass.isInstance(obj)) { 197 providers.addElement(obj); 198 return true; 199 } 200 } 201 catch (ClassNotFoundException e) { } 202 catch (InstantiationException e) { } 203 catch (IllegalAccessException e) { } 204 catch (LinkageError e) { } 205 } 206 classNames = null; 207 } 208 } 209 210 private static final int START = 0; 211 private static final int IN_NAME = 1; 212 private static final int IN_COMMENT = 2; 213 214 private static Enumeration parseConfigFile(URL url) { 215 try { 216 InputStream in = url.openStream(); 217 Reader r; 218 try { 219 r = new InputStreamReader(in, "UTF-8"); 220 } 221 catch (UnsupportedEncodingException e) { 222 r = new InputStreamReader(in, "UTF8"); 223 } 224 r = new BufferedReader(r); 225 Vector tokens = new Vector(); 226 StringBuffer tokenBuf = new StringBuffer(); 227 int state = START; 228 for (;;) { 229 int n = r.read(); 230 if (n < 0) 231 break; 232 char c = (char)n; 233 switch (c) { 234 case '\r': 235 case '\n': 236 state = START; 237 break; 238 case ' ': 239 case '\t': 240 break; 241 case '#': 242 state = IN_COMMENT; 243 break; 244 default: 245 if (state != IN_COMMENT) { 246 state = IN_NAME; 247 tokenBuf.append(c); 248 } 249 break; 250 } 251 if (tokenBuf.length() != 0 && state != IN_NAME) { 252 tokens.addElement(tokenBuf.toString()); 253 tokenBuf.setLength(0); 254 } 255 } 256 if (tokenBuf.length() != 0) 257 tokens.addElement(tokenBuf.toString()); 258 return tokens.elements(); 259 } 260 catch (IOException e) { 261 return null; 262 } 263 } 264 } 265 266 } 267