]> sjero.net Git - linphone/blob - p2pproxy/dependencies-src/jxse-src-2.5/api/src/net/jxta/util/ClassFactory.java
7d6057f6a0f41b024f5f006c0dfb64fa8c41ac2e
[linphone] / p2pproxy / dependencies-src / jxse-src-2.5 / api / src / net / jxta / util / ClassFactory.java
1 /*
2  * Copyright (c) 2001-2007 Sun Microsystems, Inc.  All rights reserved.
3  *  
4  *  The Sun Project JXTA(TM) Software License
5  *  
6  *  Redistribution and use in source and binary forms, with or without 
7  *  modification, are permitted provided that the following conditions are met:
8  *  
9  *  1. Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *  
12  *  2. Redistributions in binary form must reproduce the above copyright notice, 
13  *     this list of conditions and the following disclaimer in the documentation 
14  *     and/or other materials provided with the distribution.
15  *  
16  *  3. The end-user documentation included with the redistribution, if any, must 
17  *     include the following acknowledgment: "This product includes software 
18  *     developed by Sun Microsystems, Inc. for JXTA(TM) technology." 
19  *     Alternately, this acknowledgment may appear in the software itself, if 
20  *     and wherever such third-party acknowledgments normally appear.
21  *  
22  *  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must 
23  *     not be used to endorse or promote products derived from this software 
24  *     without prior written permission. For written permission, please contact 
25  *     Project JXTA at http://www.jxta.org.
26  *  
27  *  5. Products derived from this software may not be called "JXTA", nor may 
28  *     "JXTA" appear in their name, without prior written permission of Sun.
29  *  
30  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31  *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
32  *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN 
33  *  MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
34  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
35  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
36  *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
37  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
38  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
39  *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *  
41  *  JXTA is a registered trademark of Sun Microsystems, Inc. in the United 
42  *  States and other countries.
43  *  
44  *  Please see the license information page at :
45  *  <http://www.jxta.org/project/www/license.html> for instructions on use of 
46  *  the license in source files.
47  *  
48  *  ====================================================================
49  *  
50  *  This software consists of voluntary contributions made by many individuals 
51  *  on behalf of Project JXTA. For more information on Project JXTA, please see 
52  *  http://www.jxta.org.
53  *  
54  *  This license is based on the BSD license adopted by the Apache Foundation. 
55  */
56
57 package net.jxta.util;
58
59
60 import java.io.BufferedReader;
61 import java.io.InputStreamReader;
62 import java.net.URI;
63 import java.net.URL;
64 import java.util.Collections;
65 import java.util.Enumeration;
66 import java.util.Iterator;
67 import java.util.Map;
68 import java.util.Set;
69 import java.util.ResourceBundle;
70 import java.util.logging.Level;
71 import java.util.logging.Logger;
72
73 import java.io.IOException;
74 import java.io.InputStream;
75 import java.net.URISyntaxException;
76 import java.util.Arrays;
77 import java.util.List;
78 import java.util.MissingResourceException;
79 import java.util.NoSuchElementException;
80
81 import net.jxta.logging.Logging;
82
83
84 /**
85  * This util class provides methods needed by class construction factories.
86  *
87  * @see net.jxta.document.StructuredDocumentFactory
88  * @see net.jxta.document.AdvertisementFactory
89  * @see net.jxta.id.IDFactory
90  * @see net.jxta.endpoint.WireFormatMessageFactory
91  */
92 public abstract class ClassFactory<K, I> {
93
94     /**
95      *  Logger
96      */
97     private static final transient Logger LOG = Logger.getLogger(ClassFactory.class.getName());
98         
99     /**
100      *  Standard constructor.
101      */
102     protected ClassFactory() {}
103
104     /**
105      *  Used by ClassFactory methods to get the mapping of keys to constructors.
106      *
107      *  @return the map containing the mappings.
108      */
109     protected abstract Map<K, I> getAssocTable();
110
111     /**
112      *  Used by ClassFactory methods to ensure that all keys used with the
113      *  mapping are of the correct type.
114      *
115      *  @return Class object of the key type.
116      */
117     protected abstract Class<K> getClassForKey();
118
119     /**
120      *  Return all of the available keys for this factory.
121      *
122      *  @return Iterator of all the available keys for this factory.
123      */
124     public Iterator<K> getAvailableKeys() {
125         return Collections.unmodifiableSet(getAssocTable().keySet()).iterator();
126     }
127
128     /**
129      *  Returns an unmodifiable Set containing all of the associations 
130      *  stored in this ClassFactory.
131      *
132      *  @return Set containing all of the available entries for this factory.
133      */
134     public Set<Map.Entry<K, I>> getEntrySet() {
135         return Collections.unmodifiableSet(getAssocTable().entrySet());
136     }
137
138     /**
139      *  Used by ClassFactory methods to ensure that all of the instance classes
140      *  which register with this factory have the correct base class
141      *
142      *  @return Class object of the "Factory" type.
143      */
144     protected abstract Class<I> getClassOfInstantiators();
145
146     /**
147      *  Given a resource bundle identifier and a property name register instance
148      *  classes. The property must be a string containing class names which must
149      *  be found on the current class path. The class names are separated by
150      *  spaces.
151      *
152      *  @param resourceName name of the resource bundle
153      *  @param propertyName name of the property.
154      *  @return boolean true if at least one instance class could be registered 
155      *  with this factory.
156      *  @exception MissingResourceException if the resource bundle or
157      *  property cannot be located.
158      */
159     protected boolean registerFromResources(String resourceName, String propertyName) throws MissingResourceException {
160         ResourceBundle jxtaRsrcs = ResourceBundle.getBundle(resourceName);
161         String fromProps = jxtaRsrcs.getString(propertyName).trim();
162
163         return registerFromString(fromProps);
164     }
165
166     /**
167      *  Register instance classes given a string containing class names which
168      *  must be found on the current class path. The class names are separated
169      *  by spaces.
170      *
171      *  @param classNamesString The class name list
172      *  @return boolean true if at least one of the instance classes could be
173      *  registered otherwise false.
174      */
175     protected boolean registerFromString(String classNamesString) {
176         boolean registeredSomething = false;
177
178         if ((null == classNamesString) || (0 == classNamesString.length())) {
179             return false;
180         }
181
182         // make sure the static initialisers for each instance class are called.
183         List<String> instanceClasses = Arrays.asList(classNamesString.split("\\s"));
184         
185         for (String eachInstanceClass : instanceClasses) {
186             try {
187                 registeredSomething |= registerAssoc(eachInstanceClass);
188             } catch (Exception allElse) {
189                 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
190                     LOG.log(Level.WARNING, "Failed to register \'" + eachInstanceClass + "\'", allElse);
191                 }
192             }
193         }
194
195         return registeredSomething;
196     }
197
198     /**
199      * Given an provider interface name register instance classes.  The class
200      * path is searched for service provider lists as described by the JAR File
201      * specification for service providers
202      *
203      * @param interfaceName name of the implemented interface.
204      * @return boolean true if at least one instance class could be registered
205      *         with this factory.
206      */
207     protected boolean registerProviders(String interfaceName) {
208         ClassLoader loader = getClass().getClassLoader();
209         boolean registeredSomething = false;
210
211         try {
212             Enumeration<URL> providerLists = loader.getResources("META-INF/services/" + interfaceName);
213
214             while (providerLists.hasMoreElements()) {
215                 try {
216                     URI providerList = providerLists.nextElement().toURI();
217
218                     registeredSomething |= registerFromFile(providerList);
219                 } catch (URISyntaxException badURI) {
220                     if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
221                         LOG.log(Level.WARNING, "Failed to convert service URI", badURI);
222                     }
223                 }
224             }
225         } catch (IOException ex) {
226             if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
227                 LOG.log(Level.WARNING, "Failed to locate provider lists", ex);
228             }
229         }
230
231         return registeredSomething;
232     }
233
234     /**
235      * Register instance classes given a URI to a file containing class names
236      * which must be found on the current class path. The class names are listed
237      * on separate lines.  Comments are marked with a '#', the pound sign and
238      * any following text on any line in the file are ignored.
239      *
240      * @param providerList the URI to a file containing a list of providers
241      * @return boolean true if at least one of the instance classes could be
242      * registered otherwise false.
243      */
244     protected boolean registerFromFile(URI providerList) {
245         boolean registeredSomething = false;
246         InputStream urlStream = null;
247         
248         try {
249             urlStream = providerList.toURL().openStream();
250             BufferedReader reader = new BufferedReader(new InputStreamReader(urlStream, "UTF-8"));
251
252             String provider;
253
254             while ((provider = reader.readLine()) != null) {
255                 int comment = provider.indexOf('#');
256
257                 if (comment != -1) {
258                     provider = provider.substring(0, comment);
259                 }
260                     
261                 provider = provider.trim();
262
263                 if (provider.length() > 0) {
264                     try {
265                         registeredSomething |= registerAssoc(provider);
266                     } catch (Exception allElse) {
267                         if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
268                             LOG.log(Level.WARNING, "Failed to register \'" + provider + "\'", allElse);
269                         }
270                     }
271                 }
272             }
273         } catch (IOException ex) {
274             LOG.log(Level.WARNING, "Failed to read provider list " + providerList, ex);
275             return false;
276         } finally {
277             if(null != urlStream) {
278                 try {
279                     urlStream.close();
280                 } catch(IOException ignored) {
281                     
282                 }
283             }
284         }
285
286         return registeredSomething;
287     }
288
289     /**
290      *  Register a class with the factory from its class name. Since class name
291      *  doesn't tell us much, we just load the class and hope that something
292      *  happens as a result of the class loading. This class is often overridden
293      *  in class factories to interogate the instance class before registering
294      *  the instance class.
295      *
296      *  @param className The class name which will be registered.
297      *  @return boolean true if the class was registered otherwise false.
298      *  @throws Exception   when an error occurs.
299      */
300     protected boolean registerAssoc(final String className) throws Exception {
301
302         boolean registeredSomething = false;
303
304         try {
305             /*
306              * This implementation skankily assumes that the class registers 
307              * itself as part of class initialization.
308              */
309                         
310             Class ignored = Class.forName(className);
311                         
312             registeredSomething = true;
313         } catch (ClassNotFoundException ignored) {
314             if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
315                 LOG.warning("Failed to locate \'" + className + "\'");
316             }
317         } catch (NoClassDefFoundError ignored) {
318             if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
319                 LOG.warning("Failed to locate \'" + className + "\'");
320             }
321         }
322
323         return registeredSomething;
324     }
325
326     /**
327      *  Register a key and instance class with the factory.
328      *
329      *  @param key The key to register.
330      *  @param instantiator The instantiator object which will be registered for this key.
331      *  @return boolean true if the key was successfully registered otherwise false.
332      */
333     protected boolean registerAssoc(final K key, final I instantiator) {
334
335         // Check the association table to make sure this key is not already present.
336         if (null != getAssocTable().get(key)) {
337             return false;
338         }
339
340         getAssocTable().put(key, instantiator);
341
342         if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
343             LOG.fine("Factory : " + getClass().getName() + " Registered instantiator \'" + instantiator + "\' for \'" + key + "\'");
344         }
345
346         return true;
347     }
348
349     /**
350      *  Return the instantiator associated with the provided key.
351      *
352      *  @param key The identifier for the Instantiator class to be returned.
353      *  @return Instantiator Instantiator matching the provided key
354      *  @throws NoSuchElementException if the key has not been registered.
355      */
356     protected I getInstantiator(final K key) throws NoSuchElementException {
357
358         // Get the constructors for this key.
359         I instantiator = getAssocTable().get(key);
360
361         if (null == instantiator) {
362             throw new NoSuchElementException("key '" + key + "' not registered.");
363         }
364
365         return instantiator;
366     }
367 }