Home | History | Annotate | Download | only in jbosh
      1 /*
      2  * Copyright 2009 Mike Cumings
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *   http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.kenai.jbosh;
     18 
     19 import java.io.BufferedReader;
     20 import java.io.Closeable;
     21 import java.io.IOException;
     22 import java.io.InputStream;
     23 import java.io.InputStreamReader;
     24 import java.net.URL;
     25 import java.util.ArrayList;
     26 import java.util.List;
     27 import java.util.logging.Level;
     28 import java.util.logging.Logger;
     29 
     30 /**
     31  * Utility library for use in loading services using the Jar Service
     32  * Provider Interface (Jar SPI).  This can be replaced once the minimum
     33  * java rev moves beyond Java 5.
     34  */
     35 final class ServiceLib {
     36 
     37     /**
     38      * Logger.
     39      */
     40     private static final Logger LOG =
     41             Logger.getLogger(ServiceLib.class.getName());
     42 
     43     ///////////////////////////////////////////////////////////////////////////
     44     // Package-private methods:
     45 
     46     /**
     47      * Prevent construction.
     48      */
     49     private ServiceLib() {
     50         // Empty
     51     }
     52 
     53     ///////////////////////////////////////////////////////////////////////////
     54     // Package-private methods:
     55 
     56     /**
     57      * Probe for and select an implementation of the specified service
     58      * type by using the a modified Jar SPI mechanism.  Modified in that
     59      * the system properties will be checked to see if there is a value
     60      * set for the naem of the class to be loaded.  If so, that value is
     61      * treated as the class name of the first implementation class to be
     62      * attempted to be loaded.  This provides a (unsupported) mechanism
     63      * to insert other implementations.  Note that the supported mechanism
     64      * is by properly ordering the classpath.
     65      *
     66      * @return service instance
     67      * @throws IllegalStateException is no service implementations could be
     68      *  instantiated
     69      */
     70     static <T> T loadService(Class<T> ofType) {
     71         List<String> implClasses = loadServicesImplementations(ofType);
     72         for (String implClass : implClasses) {
     73             T result = attemptLoad(ofType, implClass);
     74             if (result != null) {
     75                 if (LOG.isLoggable(Level.FINEST)) {
     76                     LOG.finest("Selected " + ofType.getSimpleName()
     77                             + " implementation: "
     78                             + result.getClass().getName());
     79                 }
     80                 return result;
     81             }
     82         }
     83         throw(new IllegalStateException(
     84                 "Could not load " + ofType.getName() + " implementation"));
     85     }
     86 
     87     ///////////////////////////////////////////////////////////////////////////
     88     // Private methods:
     89 
     90     /**
     91      * Generates a list of implementation class names by using
     92      * the Jar SPI technique.  The order in which the class names occur
     93      * in the service manifest is significant.
     94      *
     95      * @return list of all declared implementation class names
     96      */
     97     private static List<String> loadServicesImplementations(
     98             final Class ofClass) {
     99         List<String> result = new ArrayList<String>();
    100 
    101         // Allow a sysprop to specify the first candidate
    102         String override = System.getProperty(ofClass.getName());
    103         if (override != null) {
    104             result.add(override);
    105         }
    106 
    107         ClassLoader loader = ServiceLib.class.getClassLoader();
    108         URL url = loader.getResource("META-INF/services/" + ofClass.getName());
    109         InputStream inStream = null;
    110         InputStreamReader reader = null;
    111         BufferedReader bReader = null;
    112         try {
    113             inStream = url.openStream();
    114             reader = new InputStreamReader(inStream);
    115             bReader = new BufferedReader(reader);
    116             String line;
    117             while ((line = bReader.readLine()) != null) {
    118                 if (!line.matches("\\s*(#.*)?")) {
    119                     // not a comment or blank line
    120                     result.add(line.trim());
    121                 }
    122             }
    123         } catch (IOException iox) {
    124             LOG.log(Level.WARNING,
    125                     "Could not load services descriptor: " + url.toString(),
    126                     iox);
    127         } finally {
    128             finalClose(bReader);
    129             finalClose(reader);
    130             finalClose(inStream);
    131         }
    132         return result;
    133     }
    134 
    135     /**
    136      * Attempts to load the specified implementation class.
    137      * Attempts will fail if - for example - the implementation depends
    138      * on a class not found on the classpath.
    139      *
    140      * @param className implementation class to attempt to load
    141      * @return service instance, or {@code null} if the instance could not be
    142      *  loaded
    143      */
    144     private static <T> T attemptLoad(
    145             final Class<T> ofClass,
    146             final String className) {
    147         if (LOG.isLoggable(Level.FINEST)) {
    148             LOG.finest("Attempting service load: " + className);
    149         }
    150         Level level;
    151         Exception thrown;
    152         try {
    153             Class clazz = Class.forName(className);
    154             if (!ofClass.isAssignableFrom(clazz)) {
    155                 if (LOG.isLoggable(Level.WARNING)) {
    156                     LOG.warning(clazz.getName() + " is not assignable to "
    157                             + ofClass.getName());
    158                 }
    159                 return null;
    160             }
    161             return ofClass.cast(clazz.newInstance());
    162         } catch (ClassNotFoundException ex) {
    163             level = Level.FINEST;
    164             thrown = ex;
    165         } catch (InstantiationException ex) {
    166             level = Level.WARNING;
    167             thrown = ex;
    168         } catch (IllegalAccessException ex) {
    169             level = Level.WARNING;
    170             thrown = ex;
    171         }
    172         LOG.log(level,
    173                 "Could not load " + ofClass.getSimpleName()
    174                 + " instance: " + className,
    175                 thrown);
    176         return null;
    177     }
    178 
    179     /**
    180      * Check and close a closeable object, trapping and ignoring any
    181      * exception that might result.
    182      *
    183      * @param closeMe the thing to close
    184      */
    185     private static void finalClose(final Closeable closeMe) {
    186         if (closeMe != null) {
    187             try {
    188                 closeMe.close();
    189             } catch (IOException iox) {
    190                 LOG.log(Level.FINEST, "Could not close: " + closeMe, iox);
    191             }
    192         }
    193     }
    194 
    195 }
    196