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-20 22:11:46 -0500 (Fri, 20 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 }