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