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