2 * Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
4 * The Sun Project JXTA(TM) Software License
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
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.
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.
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.
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.
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.
41 * JXTA is a registered trademark of Sun Microsystems, Inc. in the United
42 * States and other countries.
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.
48 * ====================================================================
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.
54 * This license is based on the BSD license adopted by the Apache Foundation.
57 package net.jxta.util;
60 import java.io.BufferedReader;
61 import java.io.InputStreamReader;
64 import java.util.Collections;
65 import java.util.Enumeration;
66 import java.util.Iterator;
69 import java.util.ResourceBundle;
70 import java.util.logging.Level;
71 import java.util.logging.Logger;
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;
81 import net.jxta.logging.Logging;
85 * This util class provides methods needed by class construction factories.
87 * @see net.jxta.document.StructuredDocumentFactory
88 * @see net.jxta.document.AdvertisementFactory
89 * @see net.jxta.id.IDFactory
90 * @see net.jxta.endpoint.WireFormatMessageFactory
92 public abstract class ClassFactory<K, I> {
97 private static final transient Logger LOG = Logger.getLogger(ClassFactory.class.getName());
100 * Standard constructor.
102 protected ClassFactory() {}
105 * Used by ClassFactory methods to get the mapping of keys to constructors.
107 * @return the map containing the mappings.
109 protected abstract Map<K, I> getAssocTable();
112 * Used by ClassFactory methods to ensure that all keys used with the
113 * mapping are of the correct type.
115 * @return Class object of the key type.
117 protected abstract Class<K> getClassForKey();
120 * Return all of the available keys for this factory.
122 * @return Iterator of all the available keys for this factory.
124 public Iterator<K> getAvailableKeys() {
125 return Collections.unmodifiableSet(getAssocTable().keySet()).iterator();
129 * Returns an unmodifiable Set containing all of the associations
130 * stored in this ClassFactory.
132 * @return Set containing all of the available entries for this factory.
134 public Set<Map.Entry<K, I>> getEntrySet() {
135 return Collections.unmodifiableSet(getAssocTable().entrySet());
139 * Used by ClassFactory methods to ensure that all of the instance classes
140 * which register with this factory have the correct base class
142 * @return Class object of the "Factory" type.
144 protected abstract Class<I> getClassOfInstantiators();
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
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
156 * @exception MissingResourceException if the resource bundle or
157 * property cannot be located.
159 protected boolean registerFromResources(String resourceName, String propertyName) throws MissingResourceException {
160 ResourceBundle jxtaRsrcs = ResourceBundle.getBundle(resourceName);
161 String fromProps = jxtaRsrcs.getString(propertyName).trim();
163 return registerFromString(fromProps);
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
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.
175 protected boolean registerFromString(String classNamesString) {
176 boolean registeredSomething = false;
178 if ((null == classNamesString) || (0 == classNamesString.length())) {
182 // make sure the static initialisers for each instance class are called.
183 List<String> instanceClasses = Arrays.asList(classNamesString.split("\\s"));
185 for (String eachInstanceClass : instanceClasses) {
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);
195 return registeredSomething;
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
203 * @param interfaceName name of the implemented interface.
204 * @return boolean true if at least one instance class could be registered
207 protected boolean registerProviders(String interfaceName) {
208 ClassLoader loader = getClass().getClassLoader();
209 boolean registeredSomething = false;
212 Enumeration<URL> providerLists = loader.getResources("META-INF/services/" + interfaceName);
214 while (providerLists.hasMoreElements()) {
216 URI providerList = providerLists.nextElement().toURI();
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);
225 } catch (IOException ex) {
226 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
227 LOG.log(Level.WARNING, "Failed to locate provider lists", ex);
231 return registeredSomething;
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.
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.
244 protected boolean registerFromFile(URI providerList) {
245 boolean registeredSomething = false;
246 InputStream urlStream = null;
249 urlStream = providerList.toURL().openStream();
250 BufferedReader reader = new BufferedReader(new InputStreamReader(urlStream, "UTF-8"));
254 while ((provider = reader.readLine()) != null) {
255 int comment = provider.indexOf('#');
258 provider = provider.substring(0, comment);
261 provider = provider.trim();
263 if (provider.length() > 0) {
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);
273 } catch (IOException ex) {
274 LOG.log(Level.WARNING, "Failed to read provider list " + providerList, ex);
277 if(null != urlStream) {
280 } catch(IOException ignored) {
286 return registeredSomething;
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.
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.
300 protected boolean registerAssoc(final String className) throws Exception {
302 boolean registeredSomething = false;
306 * This implementation skankily assumes that the class registers
307 * itself as part of class initialization.
310 Class ignored = Class.forName(className);
312 registeredSomething = true;
313 } catch (ClassNotFoundException ignored) {
314 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
315 LOG.warning("Failed to locate \'" + className + "\'");
317 } catch (NoClassDefFoundError ignored) {
318 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
319 LOG.warning("Failed to locate \'" + className + "\'");
323 return registeredSomething;
327 * Register a key and instance class with the factory.
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.
333 protected boolean registerAssoc(final K key, final I instantiator) {
335 // Check the association table to make sure this key is not already present.
336 if (null != getAssocTable().get(key)) {
340 getAssocTable().put(key, instantiator);
342 if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
343 LOG.fine("Factory : " + getClass().getName() + " Registered instantiator \'" + instantiator + "\' for \'" + key + "\'");
350 * Return the instantiator associated with the provided key.
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.
356 protected I getInstantiator(final K key) throws NoSuchElementException {
358 // Get the constructors for this key.
359 I instantiator = getAssocTable().get(key);
361 if (null == instantiator) {
362 throw new NoSuchElementException("key '" + key + "' not registered.");