summaryrefslogtreecommitdiffstats
path: root/sca-java-2.x/tags/2.0.1-RC1/modules/sca-api/src/main/java/org/oasisopen/sca/client/impl/SCAClientFactoryFinderImpl.java
blob: 14a02c8b8f4859c286ba76876870082480b6d345 (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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
/*
 * Copyright(C) OASIS(R) 2005,2010. All Rights Reserved.
 * OASIS trademark, IPR and other policies apply.
 */
package org.oasisopen.sca.client.impl;

import org.oasisopen.sca.client.SCAClientFactoryFinder;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Properties;

import org.oasisopen.sca.NoSuchDomainException;
import org.oasisopen.sca.ServiceRuntimeException;
import org.oasisopen.sca.client.SCAClientFactory;

/**
 * This is a default implementation of an SCAClientFactoryFinder which is 
 * used to find an implementation of the SCAClientFactory interface.
 * 
 * @see SCAClientFactoryFinder
 * @see SCAClientFactory
 * 
 * @author OASIS Open
 */
public class SCAClientFactoryFinderImpl implements 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 
     * the SPI implementation to use for the SCAClientFactory.
     */
    private static final String SCA_CLIENT_FACTORY_PROVIDER_META_INF_SERVICE 
        = "META-INF/services/" + SCA_CLIENT_FACTORY_PROVIDER_KEY;

    /**
     * Public Constructor
     */
    public SCAClientFactoryFinderImpl() {
    }
    
    /**
     * Creates an instance of the SCAClientFactorySPI implementation. 
     * This discovers the SCAClientFactorySPI 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
     * @param domainURI		URI for the Domain to which this client instance is connected
     * @return new instance of the SCAClientFactory
     * @throws ServiceRuntimeException Failed to create SCAClientFactory
     * Implementation.
     */
    public SCAClientFactory find(Properties properties,
                                 ClassLoader classLoader,
                                 URI domainURI ) 
    	throws NoSuchDomainException, ServiceRuntimeException {
		if (classLoader == null) {
			classLoader = getThreadContextClassLoader ();
		}
		final String factoryImplClassName =
			discoverProviderFactoryImplClass(properties, classLoader);
		final Class<? extends SCAClientFactory> factoryImplClass
			= loadProviderFactoryClass(factoryImplClassName, 
					                   classLoader);
		final SCAClientFactory factory =
			instantiateSCAClientFactoryClass(factoryImplClass,
								   domainURI, properties );
		return factory;
	}
    
    /**
     * Gets the Context ClassLoader for the current Thread.
     * 
     * @return The Context ClassLoader for the current Thread.
     */
    private static ClassLoader getThreadContextClassLoader () {
        return AccessController.doPrivileged(
                new PrivilegedAction<ClassLoader>() {
             public ClassLoader run() {
                 return Thread.currentThread().getContextClassLoader();
             }
         });
    }

    /**
     * Attempts to discover the class name for the SCAClientFactorySPI
     * implementation from the specified Properties, the System Properties
     * or the specified ClassLoader.
     * 
     * @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 The class name of the SCAClientFactorySPI implementation
     * @throw ServiceRuntimeException Failed to find implementation for 
     * SCAClientFactorySPI.
     */
    private static String 
        discoverProviderFactoryImplClass(Properties properties, 
    		                             ClassLoader classLoader) 
        throws ServiceRuntimeException {
        String providerClassName = 
        	checkPropertiesForSPIClassName(properties);
        if (providerClassName != null) {
            return providerClassName;
        }

        providerClassName = 
        	checkPropertiesForSPIClassName(System.getProperties());
        if (providerClassName != null) {
            return providerClassName;
        }
        
        providerClassName = checkMETAINFServicesForSPIClassName(classLoader);
        if (providerClassName == null) {
            throw new ServiceRuntimeException(
            	"Failed to find implementation for SCAClientFactory");
        }
        
        return providerClassName;
    }
    
    /**
     * Attempts to find the class name for the SCAClientFactorySPI 
     * implementation from the specified Properties.
     * 
     * @param properties    Properties that may be used when creating a new 
     * instance of the SCAClient
     * @return The class name for the SCAClientFactorySPI 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 SCAClientFactorySPI 
     * implementation from the META-INF/services directory
     * 
     * @param cl   ClassLoader that may be used when creating a new 
     * instance of the SCAClient
     * @return The class name for the SCAClientFactorySPI implementation or
     * <code>null</code> if not found.
     */
    private static String checkMETAINFServicesForSPIClassName(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 ServiceRuntimeException(
            		"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
     * @param classLoader   ClassLoader that may be used when creating a new 
     * instance of the SCAClient
     * @return The specified SCAClientFactory Implementation class
     * @throws ServiceRuntimeException Failed to load the SCAClientFactory 
     * Implementation class 
     */
    private static Class<? extends SCAClientFactory> 
        loadProviderFactoryClass(String factoryImplClassName, 
        		                 ClassLoader classLoader) 
        throws ServiceRuntimeException {

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

    /**
     * Instantiate an instance of the specified SCAClientFactorySPI 
     * Implementation class.
     *  
     * @param factoryImplClass The SCAClientFactorySPI Implementation 
     * class to instantiate.
     * @param domainURI		URI for the Domain to which this client instance is connected
     * @param properties    Properties that may be used when creating a new 
     * instance of the SCAClient
     * @return An instance of the SCAClientFactorySPI Implementation class
     * @throws ServiceRuntimeException Failed to instantiate the specified 
     * specified SCAClientFactorySPI Implementation class  
     */
    private static SCAClientFactory instantiateSCAClientFactoryClass(
    			Class<? extends SCAClientFactory> factoryImplClass,
        		URI domainURI, Properties properties)
        throws NoSuchDomainException, ServiceRuntimeException {
        
        try {
            Constructor<? extends SCAClientFactory> URIConstructor = 
            	factoryImplClass.getConstructor(URI.class, Properties.class);
            SCAClientFactory provider = 
               URIConstructor.newInstance( domainURI, properties );
            return provider;
        } catch (Throwable ex) {
            throw new ServiceRuntimeException(
               "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 ServiceRuntimeException("Failed to close stream", 
                		                          ex);
            }
        }
    }
}