1 package org.nnsoft.commons.bspi;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.io.Closeable;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.InputStreamReader;
23 import java.io.Reader;
24 import java.net.URL;
25 import java.nio.charset.Charset;
26 import java.util.Enumeration;
27 import java.util.Iterator;
28 import java.util.LinkedHashMap;
29 import java.util.NoSuchElementException;
30
31
32
33
34
35 final class ServiceClassIterator<S>
36 implements Iterator<Class<? extends S>>
37 {
38
39
40
41
42 private static final Charset UTF_8 = Charset.forName( "UTF-8" );
43
44
45
46
47 private final Class<S> service;
48
49
50
51
52 private final ClassLoader classLoader;
53
54 private final Enumeration<URL> serviceResources;
55
56
57
58
59 private final LinkedHashMap<String, Class<? extends S>> providerTypes;
60
61 private Iterator<String> pending = null;
62
63 private String nextName = null;
64
65
66
67
68
69
70
71 public ServiceClassIterator( Class<S> service, ClassLoader classLoader, Enumeration<URL> serviceResources,
72 LinkedHashMap<String, Class<? extends S>> providerTypes )
73 {
74 this.service = service;
75 this.classLoader = classLoader;
76 this.serviceResources = serviceResources;
77 this.providerTypes = providerTypes;
78 }
79
80
81
82
83 public boolean hasNext()
84 {
85 if ( nextName != null )
86 {
87 return true;
88 }
89
90 while ( ( pending == null ) || !pending.hasNext() )
91 {
92 if ( !serviceResources.hasMoreElements() )
93 {
94 return false;
95 }
96 pending = parseServiceFile( serviceResources.nextElement() );
97 }
98
99 nextName = pending.next();
100 return true;
101 }
102
103
104
105
106 public Class<? extends S> next()
107 {
108 if ( !hasNext() )
109 {
110 throw new NoSuchElementException();
111 }
112 String className = nextName;
113 nextName = null;
114 try
115 {
116 Class<?> clazz = classLoader.loadClass( className );
117 if ( !service.isAssignableFrom( clazz ) )
118 {
119 throw new ServiceConfigurationError( "Provider '" + className + "' is not assignable to Service '"
120 + service.getName() + "'" );
121 }
122 Class<? extends S> serviceClass = clazz.asSubclass( service );
123 providerTypes.put( className, serviceClass );
124 return serviceClass;
125 }
126 catch ( ClassNotFoundException e )
127 {
128 throw new ServiceConfigurationError( "Provider '" + className + "' not found", e );
129 }
130 catch ( ClassCastException e )
131 {
132 throw new ServiceConfigurationError( "Provider '"
133 + className
134 + "' is not assignable to Service '"
135 + service.getName()
136 + "'", e );
137 }
138 catch ( Throwable e )
139 {
140 throw new ServiceConfigurationError( "Provider '" + className + "' could not be instantiated", e );
141 }
142 }
143
144
145
146
147 public void remove()
148 {
149 throw new UnsupportedOperationException();
150 }
151
152
153
154
155
156
157
158
159 private Iterator<String> parseServiceFile( URL url )
160 {
161 InputStream inputStream = null;
162 Reader reader = null;
163 try
164 {
165 inputStream = url.openStream();
166 reader = new InputStreamReader( inputStream, UTF_8 );
167 ServiceFileParser<S> serviceFileParser = new ServiceFileParser<S>( reader );
168 serviceFileParser.setProviderTypes( providerTypes );
169 serviceFileParser.parse();
170 return serviceFileParser.iterator();
171 }
172 catch ( Exception e )
173 {
174 throw new ServiceConfigurationError( "An error occurred while reading service resource '"
175 + url
176 + "' for service class '"
177 + service.getName()
178 + "'", e );
179 }
180 finally
181 {
182 closeQuietly( reader );
183 closeQuietly( inputStream );
184 }
185 }
186
187
188
189
190
191
192 private static void closeQuietly( Closeable closeable )
193 {
194 if ( closeable != null )
195 {
196 try
197 {
198 closeable.close();
199 }
200 catch ( IOException e )
201 {
202
203 }
204 }
205 }
206
207 }