blob: 07d0556260f64331d18f16e1e8b9e85ea7edea54 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/*
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
17package com.kenai.jbosh;
18
19import java.io.BufferedReader;
20import java.io.Closeable;
21import java.io.IOException;
22import java.io.InputStream;
23import java.io.InputStreamReader;
24import java.net.URL;
25import java.util.ArrayList;
26import java.util.List;
27import java.util.logging.Level;
28import 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 */
35final 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}