View Javadoc

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 }