aboutsummaryrefslogtreecommitdiff
path: root/src/com/kenai/jbosh/ServiceLib.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/kenai/jbosh/ServiceLib.java')
-rw-r--r--src/com/kenai/jbosh/ServiceLib.java195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/com/kenai/jbosh/ServiceLib.java b/src/com/kenai/jbosh/ServiceLib.java
new file mode 100644
index 0000000..07d0556
--- /dev/null
+++ b/src/com/kenai/jbosh/ServiceLib.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2009 Mike Cumings
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.kenai.jbosh;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Utility library for use in loading services using the Jar Service
+ * Provider Interface (Jar SPI). This can be replaced once the minimum
+ * java rev moves beyond Java 5.
+ */
+final class ServiceLib {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOG =
+ Logger.getLogger(ServiceLib.class.getName());
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Package-private methods:
+
+ /**
+ * Prevent construction.
+ */
+ private ServiceLib() {
+ // Empty
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Package-private methods:
+
+ /**
+ * Probe for and select an implementation of the specified service
+ * type by using the a modified Jar SPI mechanism. Modified in that
+ * the system properties will be checked to see if there is a value
+ * set for the naem of the class to be loaded. If so, that value is
+ * treated as the class name of the first implementation class to be
+ * attempted to be loaded. This provides a (unsupported) mechanism
+ * to insert other implementations. Note that the supported mechanism
+ * is by properly ordering the classpath.
+ *
+ * @return service instance
+ * @throws IllegalStateException is no service implementations could be
+ * instantiated
+ */
+ static <T> T loadService(Class<T> ofType) {
+ List<String> implClasses = loadServicesImplementations(ofType);
+ for (String implClass : implClasses) {
+ T result = attemptLoad(ofType, implClass);
+ if (result != null) {
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Selected " + ofType.getSimpleName()
+ + " implementation: "
+ + result.getClass().getName());
+ }
+ return result;
+ }
+ }
+ throw(new IllegalStateException(
+ "Could not load " + ofType.getName() + " implementation"));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Private methods:
+
+ /**
+ * Generates a list of implementation class names by using
+ * the Jar SPI technique. The order in which the class names occur
+ * in the service manifest is significant.
+ *
+ * @return list of all declared implementation class names
+ */
+ private static List<String> loadServicesImplementations(
+ final Class ofClass) {
+ List<String> result = new ArrayList<String>();
+
+ // Allow a sysprop to specify the first candidate
+ String override = System.getProperty(ofClass.getName());
+ if (override != null) {
+ result.add(override);
+ }
+
+ ClassLoader loader = ServiceLib.class.getClassLoader();
+ URL url = loader.getResource("META-INF/services/" + ofClass.getName());
+ InputStream inStream = null;
+ InputStreamReader reader = null;
+ BufferedReader bReader = null;
+ try {
+ inStream = url.openStream();
+ reader = new InputStreamReader(inStream);
+ bReader = new BufferedReader(reader);
+ String line;
+ while ((line = bReader.readLine()) != null) {
+ if (!line.matches("\\s*(#.*)?")) {
+ // not a comment or blank line
+ result.add(line.trim());
+ }
+ }
+ } catch (IOException iox) {
+ LOG.log(Level.WARNING,
+ "Could not load services descriptor: " + url.toString(),
+ iox);
+ } finally {
+ finalClose(bReader);
+ finalClose(reader);
+ finalClose(inStream);
+ }
+ return result;
+ }
+
+ /**
+ * Attempts to load the specified implementation class.
+ * Attempts will fail if - for example - the implementation depends
+ * on a class not found on the classpath.
+ *
+ * @param className implementation class to attempt to load
+ * @return service instance, or {@code null} if the instance could not be
+ * loaded
+ */
+ private static <T> T attemptLoad(
+ final Class<T> ofClass,
+ final String className) {
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Attempting service load: " + className);
+ }
+ Level level;
+ Exception thrown;
+ try {
+ Class clazz = Class.forName(className);
+ if (!ofClass.isAssignableFrom(clazz)) {
+ if (LOG.isLoggable(Level.WARNING)) {
+ LOG.warning(clazz.getName() + " is not assignable to "
+ + ofClass.getName());
+ }
+ return null;
+ }
+ return ofClass.cast(clazz.newInstance());
+ } catch (ClassNotFoundException ex) {
+ level = Level.FINEST;
+ thrown = ex;
+ } catch (InstantiationException ex) {
+ level = Level.WARNING;
+ thrown = ex;
+ } catch (IllegalAccessException ex) {
+ level = Level.WARNING;
+ thrown = ex;
+ }
+ LOG.log(level,
+ "Could not load " + ofClass.getSimpleName()
+ + " instance: " + className,
+ thrown);
+ return null;
+ }
+
+ /**
+ * Check and close a closeable object, trapping and ignoring any
+ * exception that might result.
+ *
+ * @param closeMe the thing to close
+ */
+ private static void finalClose(final Closeable closeMe) {
+ if (closeMe != null) {
+ try {
+ closeMe.close();
+ } catch (IOException iox) {
+ LOG.log(Level.FINEST, "Could not close: " + closeMe, iox);
+ }
+ }
+ }
+
+}