    1   /*
    2    * Copyright 1999-2004 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.jxpath;
    17
    18  import java.io.BufferedReader;
    19  import java.io.File;
    20  import java.io.FileInputStream;
    21  import java.io.InputStream;
    22  import java.io.InputStreamReader;
    23  import java.util.Properties;
    24
    25  /***
    26   * Defines a factory API that enables applications to obtain a
    27   * JXPathContext instance.  To acquire a JXPathContext, first call the
    28   * static <code>newInstance()</code> method of JXPathContextFactory.
    29   * This method returns a concrete JXPathContextFactory.
    30   * Then call <code>newContext()</code> on that instance.  You will rarely
    31   * need to perform these steps explicitly: usually you can call one of the
    32   * <code>JXPathContex.newContext</code> methods, which will perform these steps
    33   * for you.
    34   *
    35   * @see JXPathContext#newContext(Object)
    36   * @see JXPathContext#newContext(JXPathContext,Object)
    37   *
    38   * @author Dmitri Plotnikov
    39   * @version $Revision: 1.8 $ $Date: 2004/02/29 14:17:42 $
    40   */
    41  public abstract class JXPathContextFactory {
    42
    43      /*** The default property */
    44      public static final String FACTORY_NAME_PROPERTY =
    45          "org.apache.commons.jxpath.JXPathContextFactory";
    46
    47      /*** The default factory class */
    48      private static final String DEFAULT_FACTORY_CLASS =
    49          "org.apache.commons.jxpath.ri.JXPathContextFactoryReferenceImpl";
    50
    51      /*** Avoid reading all the files when the findFactory
    52          method is called the second time ( cache the result of
    53          finding the default impl )
    54      */
    55      private static String factoryImplName = null;
    56
    57      protected JXPathContextFactory () {
    58
    59      }
    60
    61      /***
    62       * Obtain a new instance of a <code>JXPathContextFactory</code>.
    63       * This static method creates a new factory instance.
    64       * This method uses the following ordered lookup procedure to determine
    65       * the <code>JXPathContextFactory</code> implementation class to load:
    66       * <ul>
    67       * <li>
    68       * Use  the <code>org.apache.commons.jxpath.JXPathContextFactory</code>
    69       * system property.
    70       * </li>
    71       * <li>
    72       * Alternatively, use the JAVA_HOME (the parent directory where jdk is
    73       * installed)/lib/jxpath.properties for a property file that contains the
    74       * name of the implementation class keyed on
    75       * <code>org.apache.commons.jxpath.JXPathContextFactory</code>.
    76       * </li>
    77       * <li>
    78       * Use the Services API (as detailed in the JAR specification), if
    79       * available, to determine the classname. The Services API will look
    80       * for a classname in the file
    81       * <code>META- INF/services/<i>org.apache.commons.jxpath.
    82       * JXPathContextFactory</i></code> in jars available to the runtime.
    83       * </li>
    84       * <li>
    85       * Platform default <code>JXPathContextFactory</code> instance.
    86       * </li>
    87       * </ul>
    88       *
    89       * Once an application has obtained a reference to a
    90       * <code>JXPathContextFactory</code> it can use the factory to
    91       * obtain JXPathContext instances.
    92       *
    93       * @exception JXPathFactoryConfigurationError if the implementation is not
    94       * available or cannot be instantiated.
    95       */
    96      public static JXPathContextFactory newInstance() {
    97          if (factoryImplName == null) {
    98              factoryImplName =
    99                  findFactory(FACTORY_NAME_PROPERTY, DEFAULT_FACTORY_CLASS);
    100         }
    101
    102         JXPathContextFactory factoryImpl;
    103         try {
    104             Class clazz = Class.forName(factoryImplName);
    105             factoryImpl = (JXPathContextFactory) clazz.newInstance();
    106         }
    107         catch (ClassNotFoundException cnfe) {
    108             throw new JXPathContextFactoryConfigurationError(cnfe);
    109         }
    110         catch (IllegalAccessException iae) {
    111             throw new JXPathContextFactoryConfigurationError(iae);
    112         }
    113         catch (InstantiationException ie) {
    114             throw new JXPathContextFactoryConfigurationError(ie);
    115         }
    116         return factoryImpl;
    117     }
    118
    119     /***
    120      * Creates a new instance of a JXPathContext using the
    121      * currently configured parameters.
    122      *
    123      * @exception JXPathContextFactoryConfigurationError if a JXPathContext
    124      * cannot be created which satisfies the configuration requested
    125      */
    126
    127     public abstract JXPathContext newContext(
    128         JXPathContext parentContext,
    129         Object contextBean)
    130         throws JXPathContextFactoryConfigurationError;
    131
    132     // -------------------- private methods --------------------
    133     // This code is duplicated in all factories.
    134     // Keep it in sync or move it to a common place
    135     // Because it's small probably it's easier to keep it here
    136
    137     /*** Temp debug code - this will be removed after we test everything
    138      */
    139     private static boolean debug = false;
    140     static {
    141         try {
    142             debug = System.getProperty("jxpath.debug") != null;
    143         }
    144         catch (SecurityException se) {
    145             // This is ok
    146         }
    147     }
    148
    149     /*** Private implementation method - will find the implementation
    150         class in the specified order.
    151         @param property    Property name
    152         @param defaultFactory Default implementation, if nothing else is found
    153
    154         @return class name of the JXPathContextFactory
    155     */
    156     private static String findFactory(String property, String defaultFactory) {
    157         // Use the factory ID system property first
    158         try {
    159             String systemProp = System.getProperty(property);
    160             if (systemProp != null) {
    161                 if (debug) {
    162                     System.err.println(
    163                         "JXPath: found system property" + systemProp);
    164                 }
    165                 return systemProp;
    166             }
    167
    168         }
    169         catch (SecurityException se) {
    170             // Ignore
    171        }
    172
    173         // try to read from $java.home/lib/xml.properties
    174         try {
    175             String javah = System.getProperty("java.home");
    176             String configFile =
    177                 javah
    178                     + File.separator
    179                     + "lib"
    180                     + File.separator
    181                     + "jxpath.properties";
    182             File f = new File(configFile);
    183             if (f.exists()) {
    184                 Properties props = new Properties();
    185                 props.load(new FileInputStream(f));
    186                 String factory = props.getProperty(property);
    187                 if (factory != null) {
    188                     if (debug) {
    189                         System.err.println(
    190                             "JXPath: found java.home property " + factory);
    191                     }
    192                     return factory;
    193                 }
    194             }
    195         }
    196         catch (Exception ex) {
    197             if (debug) {
    198                 ex.printStackTrace();
    199             }
    200         }
    201
    202         String serviceId = "META-INF/services/" + property;
    203         // try to find services in CLASSPATH
    204         try {
    205             ClassLoader cl = JXPathContextFactory.class.getClassLoader();
    206             InputStream is = null;
    207             if (cl == null) {
    208                 is = ClassLoader.getSystemResourceAsStream(serviceId);
    209             }
    210             else {
    211                 is = cl.getResourceAsStream(serviceId);
    212             }
    213
    214             if (is != null) {
    215                 if (debug) {
    216                     System.err.println("JXPath: found  " + serviceId);
    217                 }
    218                 BufferedReader rd =
    219                     new BufferedReader(new InputStreamReader(is));
    220
    221                 String factory = rd.readLine();
    222                 rd.close();
    223
    224                 if (factory != null && !"".equals(factory)) {
    225                     if (debug) {
    226                         System.err.println(
    227                             "JXPath: loaded from services: " + factory);
    228                     }
    229                     return factory;
    230                 }
    231             }
    232         }
    233         catch (Exception ex) {
    234             if (debug) {
    235                 ex.printStackTrace();
    236             }
    237         }
    238
    239         return defaultFactory;
    240     }
    241 }