summaryrefslogtreecommitdiffstats
path: root/tags/java/sca/2.0-M3-RC1/modules/sca-api/src/main/java/org/oasisopen/sca/client/impl/SCAClientFactoryFinder.java
blob: c13e4e91420a939bd3f146632bc11d40dd00efca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/* 
 * Copyright(C) OASIS(R) 2005,2009. All Rights Reserved. 
 * OASIS trademark, IPR and other policies apply. 
 */
package org.oasisopen.sca.client.impl;

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.Properties;

import org.oasisopen.sca.SCARuntimeException;
import org.oasisopen.sca.client.SCAClientFactory;

/**
 * This is a default class that returns an SCAClientFactory implementation -
 * this class can be replaced by a vendor implementation.
 * 
 * @see SCAClientFactory
 * @author OASIS Open
 */
public class SCAClientFactoryFinder {

    /**
     * The name of the System Property used to determine the SPI implementation
     * to use for the SCAClientFactory.
     */
    private static final String SCA_CLIENT_FACTORY_PROVIDER_KEY = SCAClientFactory.class.getName();

    /**
     * The name of the file loaded from the ClassPath to determine 2488 the SPI
     * implementation to use for the SCAClientFactory. 2489
     */
    private static final String SCA_CLIENT_FACTORY_PROVIDER_META_INF_SERVICE = "META-INF/services/" + SCA_CLIENT_FACTORY_PROVIDER_KEY;

    /**
     * Private Constructor.
     */
    private SCAClientFactoryFinder() {
    }

    /**
     * Creates an instance of the SCAClientFactory implementation. This
     * discovers the SCAClientFactory implementation and instantiates the
     * provider's implementation.
     * 
     * @param properties Properties that may be used when creating a new
     *                instance of the SCAClient
     * @param classLoader ClassLoader that may be used when creating a new
     *                instance of the SCAClient
     * @return new instance of the SCAClientFactory
     * @throws SCARuntimeException Failed to create SCAClientFactory
     *                 implementation.
     */
    public static SCAClientFactory find(Properties properties, ClassLoader classLoader) {
        if (classLoader == null) {
            classLoader = getThreadContextClassLoader();
            if (classLoader == null) {
                classLoader = SCAClientFactoryFinder.class.getClassLoader();
            }
        }
        final String factoryImplClassName = discoverProviderFactoryImplClass(properties, classLoader);
        final Class<? extends SCAClientFactory> factoryImplClass = loadProviderFactoryClass(factoryImplClassName, classLoader);
        final SCAClientFactory factory = instantiateSCAClientFactoryClass(factoryImplClass);
        return factory;
    }

    /**
     * Gets the Context ClassLoader for the current Thread.
     * 
     * @return The Context ClassLoader for the current Thread.
     */
    private static ClassLoader getThreadContextClassLoader() {
        final ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader();
        return threadClassLoader;
    }

    /**
     * Attempts to discover the class name for the SCAClientFactory
     * implementation from the specified Properties, the System Properties or
     * the specified ClassLoader.
     * 
     * @return The class name of the SCAClientFactory implementation
     * @throw SCARuntimeException Failed to find implementation for
     *        SCAClientFactory.
     */
    private static String discoverProviderFactoryImplClass(Properties properties, ClassLoader classLoader) throws SCARuntimeException {
        String providerClassName = checkPropertiesForSPIClassName(properties);
        if (providerClassName != null) {
            return providerClassName;
        }

        providerClassName = checkPropertiesForSPIClassName(System.getProperties());
        if (providerClassName != null) {
            return providerClassName;
        }
        
        return checkMETAINFServicesForClassName(classLoader);
    }

    /**
     * Attempts to find the class name for the SCAClientFactory implementation
     * from the specified Properties.
     * 
     * @return The class name for the SCAClientFactory implementation or
     *         <code>null</code> if not found.
     */
    private static String checkPropertiesForSPIClassName(Properties properties) {
        if (properties == null) {
            return null;
        }

        final String providerClassName = properties.getProperty(SCA_CLIENT_FACTORY_PROVIDER_KEY);
        if (providerClassName != null && providerClassName.length() > 0) {
            return providerClassName;
        }

        return null;
    }

    /**
     * Attempts to find the class name for the SCAClientFactory implementation
     * from the META-INF/services directory
     * 
     * @return The class name for the SCAClientFactory implementation or
     *         <code>null</code> if not found.
     */
    private static String checkMETAINFServicesForClassName(ClassLoader cl) {
        final URL url = cl.getResource(SCA_CLIENT_FACTORY_PROVIDER_META_INF_SERVICE);
        if (url == null) {
            return null;
        }

        InputStream in = null;
        try {
            in = url.openStream();
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));

                String line;
                while ((line = readNextLine(reader)) != null) {
                    if (!line.startsWith("#") && line.length() > 0) {
                        return line;
                    }
                }
                return null;
            } finally {
                closeStream(reader);
            }
        } catch (IOException ex) {
            throw new SCARuntimeException("Failed to discover SCAClientFactory provider", ex);
        } finally {
            closeStream(in);
        }
    }

    /**
     * Reads the next line from the reader and returns the trimmed version of
     * that line
     * 
     * @param reader The reader from which to read the next line
     * @return The trimmed next line or <code>null</code> if the end of the
     *         stream has been reached
     * @throws IOException I/O error occurred while reading from Reader
     */
    private static String readNextLine(BufferedReader reader) throws IOException {

        String line = reader.readLine();
        if (line != null) {
            line = line.trim();
        }
        return line;
    }

    /**
     * Loads the specified SCAClientFactory implementation class.
     * 
     * @param factoryImplClassName The name of the SCAClientFactory
     *                Implementation class to load
     * @return The specified SCAClientFactory Implementation class
     * @throws SCARuntimeException Failed to load the SCAClientFactory
     *                 implementation class
     */
    private static Class<? extends SCAClientFactory> loadProviderFactoryClass(String factoryImplClassName, ClassLoader classLoader)
        throws SCARuntimeException {

        try {
            final Class<?> providerClass = classLoader.loadClass(factoryImplClassName);
            final Class<? extends SCAClientFactory> providerFactoryClass = providerClass.asSubclass(SCAClientFactory.class);
            return providerFactoryClass;
        } catch (ClassNotFoundException ex) {
            throw new SCARuntimeException("Failed to load SCAClientFactory implementation class " + factoryImplClassName, ex);
        } catch (ClassCastException ex) {
            throw new SCARuntimeException("Loaded SCAClientFactory implementation class " + factoryImplClassName
                + " is not a subclass of "
                + SCAClientFactory.class.getName(), ex);
        }
    }

    /**
     * Instantiate an instance of the specified SCAClientFactory implementation
     * class.
     * 
     * @param factoryImplClass The SCAClientFactory implementation class to
     *                instantiate.
     * @return An instance of the SCAClientFactory implementation class
     * @throws SCARuntimeException Failed to instantiate the specified specified
     *                 SCAClientFactory implementation class
     */
    private static SCAClientFactory instantiateSCAClientFactoryClass(Class<? extends SCAClientFactory> factoryImplClass) throws SCARuntimeException {

        try {
            final SCAClientFactory provider = factoryImplClass.newInstance();
            return provider;
        } catch (Throwable ex) {
            throw new SCARuntimeException("Failed to instantiate SCAClientFactory implementation class " + factoryImplClass, ex);
        }
    }

    /**
     * Utility method for closing Closeable Object.
     * 
     * @param closeable The Object to close.
     */
    private static void closeStream(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException ex) {
                throw new SCARuntimeException("Failed to close stream", ex);
            }
        }
    }
}