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