[SMX4-1475]add javax.ws.rs.client.FactoryFinder which can load ClientBuilder in OSGi container

git-svn-id: https://svn.apache.org/repos/asf/servicemix/smx4/specs/trunk@1504349 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/jsr339-api-2.0/src/main/java/javax/ws/rs/client/FactoryFinder.java b/jsr339-api-2.0/src/main/java/javax/ws/rs/client/FactoryFinder.java
new file mode 100644
index 0000000..400592b
--- /dev/null
+++ b/jsr339-api-2.0/src/main/java/javax/ws/rs/client/FactoryFinder.java
@@ -0,0 +1,219 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License").  You
+ * may not use this file except in compliance with the License.  You can
+ * obtain a copy of the License at
+ * http://glassfish.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt.  See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license."  If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above.  However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+package javax.ws.rs.client;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Factory finder utility class.
+ *
+ * @author Paul Sandoz
+ * @author Marc Hadley
+ * @since 1.0
+ */
+final class FactoryFinder {
+
+    private static final Logger LOGGER = Logger.getLogger(FactoryFinder.class.getName());
+
+    private FactoryFinder() {
+        // prevents instantiation
+    }
+
+    static ClassLoader getContextClassLoader() {
+        return AccessController.doPrivileged(
+                new PrivilegedAction<ClassLoader>() {
+
+                    @Override
+                    public ClassLoader run() {
+                        ClassLoader cl = null;
+                        try {
+                            cl = Thread.currentThread().getContextClassLoader();
+                        } catch (SecurityException ex) {
+                            LOGGER.log(
+                                    Level.WARNING,
+                                    "Unable to get context classloader instance.",
+                                    ex);
+                        }
+                        return cl;
+                    }
+                });
+    }
+
+    /**
+     * Creates an instance of the specified class using the specified
+     * <code>ClassLoader</code> object.
+     *
+     * @param className   name of the class to be instantiated.
+     * @param classLoader class loader to be used.
+     * @return instance of the specified class.
+     * @throws ClassNotFoundException if the given class could not be found
+     *                                or could not be instantiated
+     */
+    private static Object newInstance(final String className, final ClassLoader classLoader) throws ClassNotFoundException {
+        try {
+            Class spiClass;
+            if (classLoader == null) {
+                spiClass = Class.forName(className);
+            } else {
+                try {
+                    spiClass = Class.forName(className, false, classLoader);
+                } catch (ClassNotFoundException ex) {
+                    LOGGER.log(
+                            Level.FINE,
+                            "Unable to load provider class " + className
+                                    + " using custom classloader " + classLoader.getClass().getName()
+                                    + " trying again with current classloader.",
+                            ex);
+                    spiClass = Class.forName(className);
+                }
+            }
+            return spiClass.newInstance();
+        } catch (ClassNotFoundException x) {
+            throw x;
+        } catch (Exception x) {
+            throw new ClassNotFoundException("Provider " + className + " could not be instantiated: " + x, x);
+        }
+    }
+
+    /**
+     * Finds the implementation <code>Class</code> object for the given
+     * factory name, or if that fails, finds the <code>Class</code> object
+     * for the given fallback class name. The arguments supplied MUST be
+     * used in order. If using the first argument is successful, the second
+     * one will not be used.
+     * <P>
+     * This method is package private so that this code can be shared.
+     *
+     * @param factoryId         the name of the factory to find, which is
+     *                          a system property
+     * @param fallbackClassName the implementation class name, which is
+     *                          to be used only if nothing else
+     *                          is found; <code>null</code> to indicate that
+     *                          there is no fallback class name
+     * @return the <code>Class</code> object of the specified message factory;
+     *         may not be <code>null</code>
+     * @throws ClassNotFoundException if the given class could not be found
+     *                                or could not be instantiated
+     */
+    static Object find(final String factoryId, final String fallbackClassName) throws ClassNotFoundException {
+        
+        try {
+            // If we are deployed into an OSGi environment, leverage it
+            Class factoryClass = FactoryFinder.class.getClassLoader().loadClass(factoryId);
+            Class spiClass = org.apache.servicemix.specs.locator.OsgiLocator.locate(factoryClass, factoryId);
+            if (spiClass != null) {
+                return spiClass.newInstance();
+            }
+        } catch (Throwable e) {
+        }
+
+        ClassLoader classLoader = getContextClassLoader();
+
+        String serviceId = "META-INF/services/" + factoryId;
+        // try to find services in CLASSPATH
+        try {
+            InputStream is;
+            if (classLoader == null) {
+                is = ClassLoader.getSystemResourceAsStream(serviceId);
+            } else {
+                is = classLoader.getResourceAsStream(serviceId);
+            }
+
+            if (is != null) {
+                BufferedReader rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+
+                String factoryClassName = rd.readLine();
+                rd.close();
+
+                if (factoryClassName != null && !"".equals(factoryClassName)) {
+                    return newInstance(factoryClassName, classLoader);
+                }
+            }
+        } catch (Exception ex) {
+            LOGGER.log(Level.FINER, "Failed to load service " + factoryId + " from " + serviceId, ex);
+        }
+
+
+        // try to read from $java.home/lib/jaxrs.properties
+        try {
+            String javah = System.getProperty("java.home");
+            String configFile = javah + File.separator
+                    + "lib" + File.separator + "jaxrs.properties";
+            File f = new File(configFile);
+            if (f.exists()) {
+                Properties props = new Properties();
+                props.load(new FileInputStream(f));
+                String factoryClassName = props.getProperty(factoryId);
+                return newInstance(factoryClassName, classLoader);
+            }
+        } catch (Exception ex) {
+            LOGGER.log(Level.FINER, "Failed to load service " + factoryId
+                    + " from $java.home/lib/jaxrs.properties", ex);
+        }
+
+
+        // Use the system property
+        try {
+            String systemProp = System.getProperty(factoryId);
+            if (systemProp != null) {
+                return newInstance(systemProp, classLoader);
+            }
+        } catch (SecurityException se) {
+            LOGGER.log(Level.FINER, "Failed to load service " + factoryId
+                    + " from a system property", se);
+        }
+
+        if (fallbackClassName == null) {
+            throw new ClassNotFoundException(
+                    "Provider for " + factoryId + " cannot be found", null);
+        }
+
+        return newInstance(fallbackClassName, classLoader);
+    }
+}