KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > groboutils > util > classes > v1 > SPILoader


1 /*
2  * @(#)SPILoader.java
3  *
4  * Copyright (C) 2002-2003 Matt Albrecht
5  * groboclown@users.sourceforge.net
6  * http://groboutils.sourceforge.net
7  *
8  * Part of the GroboUtils package at:
9  * http://groboutils.sourceforge.net
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  */

29 package net.sourceforge.groboutils.util.classes.v1;
30
31 import java.util.Properties JavaDoc;
32 import java.util.Enumeration JavaDoc;
33 import java.util.NoSuchElementException JavaDoc;
34
35 import java.net.URL JavaDoc;
36
37 import java.io.IOException JavaDoc;
38 import java.io.InputStream JavaDoc;
39
40 import org.apache.log4j.Logger;
41
42
43 /**
44  * Loads Service Provider Interface (SPI) classes from the given classloader
45  * (if any). This will search for files in the form
46  * <tt>/META-INF/services/<i>classname</i></tt>. The discovered file will be
47  * parsed using <tt>java.util.Properties</tt> parsing method, but only the
48  * keys will be recognized (note that if a line contains no ':' or '=', then
49  * the line will be recognized as a key with an empty-string value). The found
50  * keys will be used as class names, constructed, and returned, using the
51  * <tt>ClassLoadHelper</tt> class in this package.
52  * <P>
53  * This is intended to follow the SPI interface spec, partially described in
54  * the JDK 1.4 documentation in the package docs for <tt>java.awt.im.spi</tt>.
55  * <P>
56  * Note that by using a Properties instance, this class is limited to only
57  * one instance of a class type per META-INF service file. This is assumed to
58  * be an adequate restriction, since in most circumstances, there should only
59  * be one instance of a service provider loaded per need.
60  *
61  * @author Matt Albrecht <a HREF="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
62  * @since June 28, 2002
63  * @version $Date: 2003/05/08 14:12:21 $
64  */

65 public class SPILoader
66 {
67     private static final Logger LOG = Logger.getLogger(
68         SPILoader.class.getName() );
69     
70     
71     /**
72      * List of all resource URLs to the class's SPI files.
73      */

74     private Enumeration JavaDoc spiUrls;
75     
76     /**
77      * Current SPI url's property (classname) keys.
78      */

79     private Enumeration JavaDoc propKeys = null;
80     
81     private String JavaDoc nextKey = null;
82     
83     private Class JavaDoc base;
84     
85     private ClassLoadHelper clh;
86         
87     
88     /**
89      * Use the context (thread) classloader or the system class loader.
90      *
91      * @param spiBase the base class that all loaded SPI classes must
92      * implement. This is also used to define the name of the files
93      * to load from the META-INF directory.
94      * @exception IOException if there is an I/O problem loading the
95      * initial set of resources.
96      */

97     public SPILoader( Class JavaDoc spiBase )
98             throws IOException JavaDoc
99     {
100         this( spiBase, null );
101     }
102     
103     
104     /**
105      * Create a new SPILoader, loading the service files from the given
106      * class loader classpath. If <tt>cl</tt> is <tt>null</tt>, then the
107      * context (thread) class loader or system class loader will be used
108      * instead.
109      *
110      * @param spiBase the base class that all loaded SPI classes must
111      * implement. This is also used to define the name of the files
112      * to load from the META-INF directory.
113      * @param cl classloader to load files from.
114      * @exception IOException if there is an I/O problem loading the
115      * initial set of resources.
116      */

117     public SPILoader( Class JavaDoc spiBase, ClassLoader JavaDoc cl )
118             throws IOException JavaDoc
119     {
120         if (spiBase == null)
121         {
122             throw new IllegalArgumentException JavaDoc("spiBase cannot be null.");
123         }
124         
125         
126         if (cl == null)
127         {
128             this.clh = new ClassLoadHelper( spiBase );
129         }
130         else
131         {
132             this.clh = new ClassLoadHelper( cl );
133         }
134         
135         String JavaDoc metaName = "/META-INF/services/" + spiBase.getName();
136         
137         this.spiUrls = this.clh.getResources( metaName );
138         if (this.spiUrls == null)
139         {
140             throw new IOException JavaDoc("No URLs were discovered.");
141         }
142         this.base = spiBase;
143     }
144     
145     
146     /**
147      * Discovers if there is another provider class instance to load.
148      *
149      * @return <tt>true</tt> if there is another provider, otherwise
150      * <tt>false</tt>.
151      */

152     public boolean hasNext()
153             throws IOException JavaDoc
154     {
155         while (this.nextKey == null)
156         {
157             if (this.propKeys == null || !this.propKeys.hasMoreElements())
158             {
159                 if (!this.spiUrls.hasMoreElements())
160                 {
161                     LOG.debug("No more URLs to process");
162                     return false;
163                 }
164                 URL JavaDoc url = (URL JavaDoc)this.spiUrls.nextElement();
165                 LOG.debug( "Processing next URL: "+url );
166                 Properties JavaDoc props = new Properties JavaDoc();
167                 LOG.debug( "Opening URL stream" );
168                 InputStream JavaDoc is = url.openStream();
169                 try
170                 {
171                     props.load( is );
172                 }
173                 finally
174                 {
175                     is.close();
176                 }
177                 this.propKeys = props.keys();
178                 LOG.debug( "URL contains "+props.size()+" class names" );
179             }
180             else
181             {
182                 this.nextKey = (String JavaDoc)this.propKeys.nextElement();
183                 LOG.debug( "Next classname is "+this.nextKey );
184             }
185         }
186         return true;
187     }
188     
189     
190     /**
191      * Returns a new instance of the next class name. If the defined
192      * next class is invalid, then an IOException is thrown. The returned
193      * object is guaranteed to be non-null and an instance of the constructor
194      * baseClass.
195      *
196      * @return the next provider in the list of discovered SPI providers.
197      * @exception IOException if there was an I/O error, or if the referenced
198      * resource defines an invalid class to load.
199      * @exception NoSuchElementException if the enumeration is already at
200      * the end of the list.
201      */

202     public Object JavaDoc nextProvier()
203             throws IOException JavaDoc
204     {
205         // ensure we have a next key to go to
206
hasNext();
207         
208         if (this.nextKey == null)
209         {
210             // end of list
211
throw new NoSuchElementException JavaDoc( "end of list" );
212         }
213         String JavaDoc cn = this.nextKey;
214         
215         // ensure we advance to the next key next iteration.
216
this.nextKey = null;
217         
218         Object JavaDoc o;
219         try
220         {
221             o = this.clh.createObject( cn );
222         }
223         catch (IllegalStateException JavaDoc ise)
224         {
225             LOG.info( "create object for type "+cn+" threw exception.", ise );
226             throw new IOException JavaDoc( ise.getMessage() );
227         }
228         
229         if (o == null)
230         {
231             LOG.info( "create object for type "+cn+" returned null." );
232             throw new IOException JavaDoc( "Could not create an instance of type "+
233                 cn );
234         }
235         
236         if (!this.base.isInstance( o ))
237         {
238             throw new IOException JavaDoc( "SPI defined class "+cn+
239                 ", but expected class of type "+this.base.getName() );
240         }
241         return o;
242     }
243 }
244
245
Popular Tags