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