• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
ServiceLib()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      */
loadService(Class<T> ofType)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      */
loadServicesImplementations( final Class ofClass)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      */
attemptLoad( final Class<T> ofClass, final String className)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      */
finalClose(final Closeable closeMe)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