    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *     http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.jxpath;
    018
    019    import java.io.BufferedReader;
    020    import java.io.File;
    021    import java.io.FileInputStream;
    022    import java.io.IOException;
    023    import java.io.InputStream;
    024    import java.io.InputStreamReader;
    025    import java.util.Properties;
    026
    027    /**
    028     * Defines a factory API that enables applications to obtain a
    029     * {@link JXPathContext} instance.  To acquire a JXPathContext, first call the
    030     * static {@link #newInstance} method of JXPathContextFactory.
    031     * This method returns a concrete JXPathContextFactory.
    032     * Then call {@link #newContext} on that instance.  You will rarely
    033     * need to perform these steps explicitly: usually you can call one of the
    034     * <code>JXPathContex.newContext</code> methods, which will perform these steps
    035     * for you.
    036     *
    037     * @see JXPathContext#newContext(Object)
    038     * @see JXPathContext#newContext(JXPathContext,Object)
    039     *
    040     * @author Dmitri Plotnikov
    041     * @version $Revision: 670727 $ $Date: 2008-06-23 15:10:38 -0500 (Mon, 23 Jun 2008) $
    042     */
    043    public abstract class JXPathContextFactory {
    044
    045        /** The default property */
    046        public static final String FACTORY_NAME_PROPERTY =
    047            "org.apache.commons.jxpath.JXPathContextFactory";
    048
    049        /** The default factory class */
    050        private static final String DEFAULT_FACTORY_CLASS =
    051            "org.apache.commons.jxpath.ri.JXPathContextFactoryReferenceImpl";
    052
    053        /** Avoid reading all the files when the findFactory
    054            method is called the second time ( cache the result of
    055            finding the default impl )
    056        */
    057        private static String factoryImplName = null;
    058
    059        /**
    060         * Create a new JXPathContextFactory.
    061         */
    062        protected JXPathContextFactory () {
    063
    064        }
    065
    066        /**
    067         * Obtain a new instance of a <code>JXPathContextFactory</code>.
    068         * This static method creates a new factory instance.
    069         * This method uses the following ordered lookup procedure to determine
    070         * the <code>JXPathContextFactory</code> implementation class to load:
    071         * <ul>
    072         * <li>
    073         * Use  the <code>org.apache.commons.jxpath.JXPathContextFactory</code>
    074         * system property.
    075         * </li>
    076         * <li>
    077         * Alternatively, use the JAVA_HOME (the parent directory where jdk is
    078         * installed)/lib/jxpath.properties for a property file that contains the
    079         * name of the implementation class keyed on
    080         * <code>org.apache.commons.jxpath.JXPathContextFactory</code>.
    081         * </li>
    082         * <li>
    083         * Use the Services API (as detailed in the JAR specification), if
    084         * available, to determine the classname. The Services API will look
    085         * for a classname in the file
    086         * <code>META- INF/services/<i>org.apache.commons.jxpath.
    087         * JXPathContextFactory</i></code> in jars available to the runtime.
    088         * </li>
    089         * <li>
    090         * Platform default <code>JXPathContextFactory</code> instance.
    091         * </li>
    092         * </ul>
    093         *
    094         * Once an application has obtained a reference to a
    095         * <code>JXPathContextFactory</code> it can use the factory to
    096         * obtain JXPathContext instances.
    097         *
    098         * @return JXPathContextFactory
    099         * @exception JXPathContextFactoryConfigurationError if the implementation
    100         *            is not available or cannot be instantiated.
    101         */
    102        public static JXPathContextFactory newInstance() {
    103            if (factoryImplName == null) {
    104                factoryImplName =
    105                    findFactory(FACTORY_NAME_PROPERTY, DEFAULT_FACTORY_CLASS);
    106            }
    107
    108            JXPathContextFactory factoryImpl;
    109            try {
    110                Class clazz = Class.forName(factoryImplName);
    111                factoryImpl = (JXPathContextFactory) clazz.newInstance();
    112            }
    113            catch (ClassNotFoundException cnfe) {
    114                throw new JXPathContextFactoryConfigurationError(cnfe);
    115            }
    116            catch (IllegalAccessException iae) {
    117                throw new JXPathContextFactoryConfigurationError(iae);
    118            }
    119            catch (InstantiationException ie) {
    120                throw new JXPathContextFactoryConfigurationError(ie);
    121            }
    122            return factoryImpl;
    123        }
    124
    125        /**
    126         * Creates a new instance of a JXPathContext using the
    127         * currently configured parameters.
    128         * @param parentContext parent context
    129         * @param contextBean Object bean
    130         * @return JXPathContext
    131         * @exception JXPathContextFactoryConfigurationError if a JXPathContext
    132         *            cannot be created which satisfies the configuration requested
    133         */
    134
    135        public abstract JXPathContext newContext(
    136            JXPathContext parentContext,
    137            Object contextBean);
    138
    139        // -------------------- private methods --------------------
    140        // This code is duplicated in all factories.
    141        // Keep it in sync or move it to a common place
    142        // Because it's small probably it's easier to keep it here
    143
    144        /** Temp debug code - this will be removed after we test everything
    145         */
    146        private static boolean debug = false;
    147        static {
    148            try {
    149                debug = System.getProperty("jxpath.debug") != null;
    150            }
    151            catch (SecurityException se) { //NOPMD
    152                // This is ok
    153            }
    154        }
    155
    156        /**
    157         * Private implementation method - will find the implementation
    158         * class in the specified order.
    159         * @param property    Property name
    160         * @param defaultFactory Default implementation, if nothing else is found
    161         *
    162         * @return class name of the JXPathContextFactory
    163         */
    164        private static String findFactory(String property, String defaultFactory) {
    165            // Use the factory ID system property first
    166            try {
    167                String systemProp = System.getProperty(property);
    168                if (systemProp != null) {
    169                    if (debug) {
    170                        System.err.println(
    171                            "JXPath: found system property" + systemProp);
    172                    }
    173                    return systemProp;
    174                }
    175
    176            }
    177            catch (SecurityException se) { //NOPMD
    178                // Ignore
    179           }
    180
    181            // try to read from $java.home/lib/xml.properties
    182            try {
    183                String javah = System.getProperty("java.home");
    184                String configFile =
    185                    javah
    186                        + File.separator
    187                        + "lib"
    188                        + File.separator
    189                        + "jxpath.properties";
    190                File f = new File(configFile);
    191                if (f.exists()) {
    192                    Properties props = new Properties();
    193                    FileInputStream fis = new FileInputStream(f);
    194                    try {
    195                        props.load(fis);
    196                    }
    197                    finally {
    198                        if (fis != null) {
    199                            try {
    200                                fis.close();
    201                            }
    202                            catch (IOException e) { //NOPMD
    203                                //swallow
    204                            }
    205                        }
    206                    }
    207                    String factory = props.getProperty(property);
    208                    if (factory != null) {
    209                        if (debug) {
    210                            System.err.println(
    211                                "JXPath: found java.home property " + factory);
    212                        }
    213                        return factory;
    214                    }
    215                }
    216            }
    217            catch (IOException ex) {
    218                if (debug) {
    219                    ex.printStackTrace();
    220                }
    221            }
    222
    223            String serviceId = "META-INF/services/" + property;
    224            // try to find services in CLASSPATH
    225            try {
    226                ClassLoader cl = JXPathContextFactory.class.getClassLoader();
    227                InputStream is = null;
    228                if (cl == null) {
    229                    is = ClassLoader.getSystemResourceAsStream(serviceId);
    230                }
    231                else {
    232                    is = cl.getResourceAsStream(serviceId);
    233                }
    234
    235                if (is != null) {
    236                    if (debug) {
    237                        System.err.println("JXPath: found  " + serviceId);
    238                    }
    239                    BufferedReader rd =
    240                        new BufferedReader(new InputStreamReader(is));
    241
    242                    String factory = null;
    243                    try {
    244                        factory = rd.readLine();
    245                    }
    246                    finally {
    247                        try {
    248                            rd.close();
    249                        }
    250                        catch (IOException e) { //NOPMD
    251                            //swallow
    252                        }
    253                    }
    254
    255                    if (factory != null && !"".equals(factory)) {
    256                        if (debug) {
    257                            System.err.println(
    258                                "JXPath: loaded from services: " + factory);
    259                        }
    260                        return factory;
    261                    }
    262                }
    263            }
    264            catch (Exception ex) {
    265                if (debug) {
    266                    ex.printStackTrace();
    267                }
    268            }
    269            return defaultFactory;
    270        }
    271    }