RowSetProvider.java revision 14360:03453120a011
155682Smarkm/*
2233294Sstas * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3233294Sstas * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4233294Sstas *
555682Smarkm * This code is free software; you can redistribute it and/or modify it
6233294Sstas * under the terms of the GNU General Public License version 2 only, as
7233294Sstas * published by the Free Software Foundation.  Oracle designates this
8233294Sstas * particular file as subject to the "Classpath" exception as provided
955682Smarkm * by Oracle in the LICENSE file that accompanied this code.
10233294Sstas *
11233294Sstas * This code is distributed in the hope that it will be useful, but WITHOUT
1255682Smarkm * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13233294Sstas * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14233294Sstas * version 2 for more details (a copy is included in the LICENSE file that
15233294Sstas * accompanied this code).
1655682Smarkm *
17233294Sstas * You should have received a copy of the GNU General Public License version
18233294Sstas * 2 along with this work; if not, write to the Free Software Foundation,
19233294Sstas * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2055682Smarkm *
21233294Sstas * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22233294Sstas * or visit www.oracle.com if you need additional information or have any
23233294Sstas * questions.
24233294Sstas */
25233294Sstas
26233294Sstaspackage javax.sql.rowset;
27233294Sstas
28233294Sstasimport java.security.AccessController;
29233294Sstasimport java.security.PrivilegedAction;
30233294Sstasimport java.sql.SQLException;
31233294Sstasimport java.util.PropertyPermission;
3255682Smarkmimport java.util.ServiceConfigurationError;
3355682Smarkmimport java.util.ServiceLoader;
3455682Smarkmimport sun.reflect.misc.ReflectUtil;
3555682Smarkm
36233294Sstas/**
3755682Smarkm * A factory API that enables applications to obtain a
38233294Sstas * {@code RowSetFactory} implementation  that can be used to create different
3955682Smarkm * types of {@code RowSet} implementations.
40233294Sstas * <p>
4155682Smarkm * Example:
4255682Smarkm * </p>
4355682Smarkm * <pre>
4455682Smarkm * RowSetFactory aFactory = RowSetProvider.newFactory();
4555682Smarkm * CachedRowSet crs = aFactory.createCachedRowSet();
46233294Sstas * ...
4755682Smarkm * RowSetFactory rsf = RowSetProvider.newFactory("com.sun.rowset.RowSetFactoryImpl", null);
4855682Smarkm * WebRowSet wrs = rsf.createWebRowSet();
4955682Smarkm * </pre>
5055682Smarkm *<p>
5155682Smarkm * Tracing of this class may be enabled by setting the System property
5255682Smarkm * {@code javax.sql.rowset.RowSetFactory.debug} to any value but {@code false}.
5355682Smarkm * </p>
5455682Smarkm *
5555682Smarkm * @author Lance Andersen
5655682Smarkm * @since 1.7
5755682Smarkm */
58233294Sstaspublic class RowSetProvider {
5955682Smarkm
6055682Smarkm    private static final String ROWSET_DEBUG_PROPERTY = "javax.sql.rowset.RowSetProvider.debug";
6155682Smarkm    private static final String ROWSET_FACTORY_IMPL = "com.sun.rowset.RowSetFactoryImpl";
6255682Smarkm    private static final String ROWSET_FACTORY_NAME = "javax.sql.rowset.RowSetFactory";
6355682Smarkm    /**
6455682Smarkm     * Internal debug flag.
6555682Smarkm     */
6655682Smarkm    private static boolean debug = true;
6755682Smarkm
6855682Smarkm
6955682Smarkm    static {
7055682Smarkm        // Check to see if the debug property is set
7155682Smarkm        String val = getSystemProperty(ROWSET_DEBUG_PROPERTY);
7255682Smarkm        // Allow simply setting the prop to turn on debug
7355682Smarkm        debug = val != null && !"false".equals(val);
7455682Smarkm    }
7555682Smarkm
7655682Smarkm    /**
7755682Smarkm     * RowSetProvider constructor
7855682Smarkm     */
7955682Smarkm    protected RowSetProvider () {
8055682Smarkm    }
81233294Sstas
8255682Smarkm    /**
83233294Sstas     * <p>Creates a new instance of a <code>RowSetFactory</code>
8455682Smarkm     * implementation.  This method uses the following
8555682Smarkm     * look up order to determine
8655682Smarkm     * the <code>RowSetFactory</code> implementation class to load:</p>
8755682Smarkm     * <ul>
8855682Smarkm     * <li>
8955682Smarkm     * The System property {@code javax.sql.rowset.RowSetFactory}.  For example:
9055682Smarkm     * <ul>
9155682Smarkm     * <li>
92     * -Djavax.sql.rowset.RowSetFactory=com.sun.rowset.RowSetFactoryImpl
93     * </li>
94     * </ul>
95     * <li>
96     * The {@link ServiceLoader} API. The {@code ServiceLoader} API will look
97     * for a class name in the file
98     * {@code META-INF/services/javax.sql.rowset.RowSetFactory}
99     * in jars available to the runtime. For example, to have the RowSetFactory
100     * implementation {@code com.sun.rowset.RowSetFactoryImpl } loaded, the
101     * entry in {@code META-INF/services/javax.sql.rowset.RowSetFactory} would be:
102     *  <ul>
103     * <li>
104     * {@code com.sun.rowset.RowSetFactoryImpl }
105     * </li>
106     * </ul>
107     * </li>
108     * <li>
109     * Platform default <code>RowSetFactory</code> instance.
110     * </li>
111     * </ul>
112     *
113     * <p>Once an application has obtained a reference to a {@code RowSetFactory},
114     * it can use the factory to obtain RowSet instances.</p>
115     *
116     * @return New instance of a <code>RowSetFactory</code>
117     *
118     * @throws SQLException if the default factory class cannot be loaded,
119     * instantiated. The cause will be set to actual Exception
120     *
121     * @see ServiceLoader
122     * @since 1.7
123     */
124    public static RowSetFactory newFactory()
125            throws SQLException {
126        // Use the system property first
127        RowSetFactory factory = null;
128        String factoryClassName = null;
129        try {
130            trace("Checking for Rowset System Property...");
131            factoryClassName = getSystemProperty(ROWSET_FACTORY_NAME);
132            if (factoryClassName != null) {
133                trace("Found system property, value=" + factoryClassName);
134                if (factoryClassName.equals(ROWSET_FACTORY_IMPL)) {
135                    return defaultRowSetFactory();
136                }
137                // getFactoryClass takes care of adding the read edge if
138                // necessary
139                @SuppressWarnings("deprecation")
140                Object o = getFactoryClass(factoryClassName, null, false).newInstance();
141                factory = (RowSetFactory) o;
142            }
143        } catch (Exception e) {
144            throw new SQLException( "RowSetFactory: " + factoryClassName +
145                    " could not be instantiated: ", e);
146        }
147
148        // Check to see if we found the RowSetFactory via a System property
149        if (factory == null) {
150            // If the RowSetFactory is not found via a System Property, now
151            // look it up via the ServiceLoader API and if not found, use the
152            // Java SE default.
153            factory = loadViaServiceLoader();
154        }
155        return  factory == null ? defaultRowSetFactory() : factory;
156    }
157
158    private static RowSetFactory defaultRowSetFactory() {
159        return new com.sun.rowset.RowSetFactoryImpl();
160    }
161
162    /**
163     * <p>Creates  a new instance of a <code>RowSetFactory</code> from the
164     * specified factory class name.
165     * This function is useful when there are multiple providers in the classpath.
166     * It gives more control to the application as it can specify which provider
167     * should be loaded.</p>
168     *
169     * <p>Once an application has obtained a reference to a <code>RowSetFactory</code>
170     * it can use the factory to obtain RowSet instances.</p>
171     *
172     * @param factoryClassName fully qualified factory class name that
173     * provides  an implementation of <code>javax.sql.rowset.RowSetFactory</code>.
174     *
175     * @param cl <code>ClassLoader</code> used to load the factory
176     * class. If <code>null</code> current <code>Thread</code>'s context
177     * classLoader is used to load the factory class.
178     *
179     * @return New instance of a <code>RowSetFactory</code>
180     *
181     * @throws SQLException if <code>factoryClassName</code> is
182     * <code>null</code>, or the factory class cannot be loaded, instantiated.
183     *
184     * @see #newFactory()
185     *
186     * @since 1.7
187     */
188    public static RowSetFactory newFactory(String factoryClassName, ClassLoader cl)
189            throws SQLException {
190
191        trace("***In newInstance()");
192
193        if(factoryClassName == null) {
194            throw new SQLException("Error: factoryClassName cannot be null");
195        }
196        try {
197            ReflectUtil.checkPackageAccess(factoryClassName);
198        } catch (java.security.AccessControlException e) {
199            throw new SQLException("Access Exception",e);
200        }
201
202        try {
203            // getFactoryClass takes care of adding the read edge if
204            // necessary
205            Class<?> providerClass = getFactoryClass(factoryClassName, cl, false);
206            @SuppressWarnings("deprecation")
207            RowSetFactory instance = (RowSetFactory) providerClass.newInstance();
208            if (debug) {
209                trace("Created new instance of " + providerClass +
210                        " using ClassLoader: " + cl);
211            }
212            return instance;
213        } catch (ClassNotFoundException x) {
214            throw new SQLException(
215                    "Provider " + factoryClassName + " not found", x);
216        } catch (Exception x) {
217            throw new SQLException(
218                    "Provider " + factoryClassName + " could not be instantiated: " + x,
219                    x);
220        }
221    }
222
223    /*
224     * Returns the class loader to be used.
225     * @return The ClassLoader to use.
226     *
227     */
228    static private ClassLoader getContextClassLoader() throws SecurityException {
229        return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
230
231            public ClassLoader run() {
232                ClassLoader cl = null;
233
234                cl = Thread.currentThread().getContextClassLoader();
235
236                if (cl == null) {
237                    cl = ClassLoader.getSystemClassLoader();
238                }
239
240                return cl;
241            }
242        });
243    }
244
245    /**
246     * Attempt to load a class using the class loader supplied. If that fails
247     * and fall back is enabled, the current (i.e. bootstrap) class loader is
248     * tried.
249     *
250     * If the class loader supplied is <code>null</code>, first try using the
251     * context class loader followed by the current class loader.
252     *  @return The class which was loaded
253     */
254    static private Class<?> getFactoryClass(String factoryClassName, ClassLoader cl,
255            boolean doFallback) throws ClassNotFoundException {
256        Class<?> factoryClass = null;
257
258        try {
259            if (cl == null) {
260                cl = getContextClassLoader();
261                if (cl == null) {
262                    throw new ClassNotFoundException();
263                } else {
264                    factoryClass = cl.loadClass(factoryClassName);
265                }
266            } else {
267                factoryClass = cl.loadClass(factoryClassName);
268            }
269        } catch (ClassNotFoundException e) {
270            if (doFallback) {
271                // Use current class loader
272                factoryClass = Class.forName(factoryClassName, true, RowSetFactory.class.getClassLoader());
273            } else {
274                throw e;
275            }
276        }
277
278        ReflectUtil.checkPackageAccess(factoryClass);
279        return factoryClass;
280    }
281
282    /**
283     * Use the ServiceLoader mechanism to load  the default RowSetFactory
284     * @return default RowSetFactory Implementation
285     */
286    static private RowSetFactory loadViaServiceLoader() throws SQLException {
287        RowSetFactory theFactory = null;
288        try {
289            trace("***in loadViaServiceLoader():");
290            for (RowSetFactory factory : ServiceLoader.load(javax.sql.rowset.RowSetFactory.class)) {
291                trace(" Loading done by the java.util.ServiceLoader :" + factory.getClass().getName());
292                theFactory = factory;
293                break;
294            }
295        } catch (ServiceConfigurationError e) {
296            throw new SQLException(
297                    "RowSetFactory: Error locating RowSetFactory using Service "
298                    + "Loader API: " + e, e);
299        }
300        return theFactory;
301
302    }
303
304    /**
305     * Returns the requested System Property.  If a {@code SecurityException}
306     * occurs, just return NULL
307     * @param propName - System property to retrieve
308     * @return The System property value or NULL if the property does not exist
309     * or a {@code SecurityException} occurs.
310     */
311    static private String getSystemProperty(final String propName) {
312        String property = null;
313        try {
314            property = AccessController.doPrivileged(new PrivilegedAction<String>() {
315
316                public String run() {
317                    return System.getProperty(propName);
318                }
319            }, null, new PropertyPermission(propName, "read"));
320        } catch (SecurityException se) {
321            trace("error getting " + propName + ":  "+ se);
322            if (debug) {
323                se.printStackTrace();
324            }
325        }
326        return property;
327    }
328
329    /**
330     * Debug routine which will output tracing if the System Property
331     * -Djavax.sql.rowset.RowSetFactory.debug is set
332     * @param msg - The debug message to display
333     */
334    private static void trace(String msg) {
335        if (debug) {
336            System.err.println("###RowSets: " + msg);
337        }
338    }
339}
340