001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.jxpath;
018
019 import java.io.BufferedReader;
020 import java.io.File;
021 import java.io.FileInputStream;
022 import java.io.IOException;
023 import java.io.InputStream;
024 import java.io.InputStreamReader;
025 import java.util.Properties;
026
027 /**
028 * Defines a factory API that enables applications to obtain a
029 * {@link JXPathContext} instance. To acquire a JXPathContext, first call the
030 * static {@link #newInstance} method of JXPathContextFactory.
031 * This method returns a concrete JXPathContextFactory.
032 * Then call {@link #newContext} on that instance. You will rarely
033 * need to perform these steps explicitly: usually you can call one of the
034 * <code>JXPathContex.newContext</code> methods, which will perform these steps
035 * for you.
036 *
037 * @see JXPathContext#newContext(Object)
038 * @see JXPathContext#newContext(JXPathContext,Object)
039 *
040 * @author Dmitri Plotnikov
041 * @version $Revision: 670727 $ $Date: 2008-06-23 15:10:38 -0500 (Mon, 23 Jun 2008) $
042 */
043 public abstract class JXPathContextFactory {
044
045 /** The default property */
046 public static final String FACTORY_NAME_PROPERTY =
047 "org.apache.commons.jxpath.JXPathContextFactory";
048
049 /** The default factory class */
050 private static final String DEFAULT_FACTORY_CLASS =
051 "org.apache.commons.jxpath.ri.JXPathContextFactoryReferenceImpl";
052
053 /** Avoid reading all the files when the findFactory
054 method is called the second time ( cache the result of
055 finding the default impl )
056 */
057 private static String factoryImplName = null;
058
059 /**
060 * Create a new JXPathContextFactory.
061 */
062 protected JXPathContextFactory () {
063
064 }
065
066 /**
067 * Obtain a new instance of a <code>JXPathContextFactory</code>.
068 * This static method creates a new factory instance.
069 * This method uses the following ordered lookup procedure to determine
070 * the <code>JXPathContextFactory</code> implementation class to load:
071 * <ul>
072 * <li>
073 * Use the <code>org.apache.commons.jxpath.JXPathContextFactory</code>
074 * system property.
075 * </li>
076 * <li>
077 * Alternatively, use the JAVA_HOME (the parent directory where jdk is
078 * installed)/lib/jxpath.properties for a property file that contains the
079 * name of the implementation class keyed on
080 * <code>org.apache.commons.jxpath.JXPathContextFactory</code>.
081 * </li>
082 * <li>
083 * Use the Services API (as detailed in the JAR specification), if
084 * available, to determine the classname. The Services API will look
085 * for a classname in the file
086 * <code>META- INF/services/<i>org.apache.commons.jxpath.
087 * JXPathContextFactory</i></code> in jars available to the runtime.
088 * </li>
089 * <li>
090 * Platform default <code>JXPathContextFactory</code> instance.
091 * </li>
092 * </ul>
093 *
094 * Once an application has obtained a reference to a
095 * <code>JXPathContextFactory</code> it can use the factory to
096 * obtain JXPathContext instances.
097 *
098 * @return JXPathContextFactory
099 * @exception JXPathContextFactoryConfigurationError if the implementation
100 * is not available or cannot be instantiated.
101 */
102 public static JXPathContextFactory newInstance() {
103 if (factoryImplName == null) {
104 factoryImplName =
105 findFactory(FACTORY_NAME_PROPERTY, DEFAULT_FACTORY_CLASS);
106 }
107
108 JXPathContextFactory factoryImpl;
109 try {
110 Class clazz = Class.forName(factoryImplName);
111 factoryImpl = (JXPathContextFactory) clazz.newInstance();
112 }
113 catch (ClassNotFoundException cnfe) {
114 throw new JXPathContextFactoryConfigurationError(cnfe);
115 }
116 catch (IllegalAccessException iae) {
117 throw new JXPathContextFactoryConfigurationError(iae);
118 }
119 catch (InstantiationException ie) {
120 throw new JXPathContextFactoryConfigurationError(ie);
121 }
122 return factoryImpl;
123 }
124
125 /**
126 * Creates a new instance of a JXPathContext using the
127 * currently configured parameters.
128 * @param parentContext parent context
129 * @param contextBean Object bean
130 * @return JXPathContext
131 * @exception JXPathContextFactoryConfigurationError if a JXPathContext
132 * cannot be created which satisfies the configuration requested
133 */
134
135 public abstract JXPathContext newContext(
136 JXPathContext parentContext,
137 Object contextBean);
138
139 // -------------------- private methods --------------------
140 // This code is duplicated in all factories.
141 // Keep it in sync or move it to a common place
142 // Because it's small probably it's easier to keep it here
143
144 /** Temp debug code - this will be removed after we test everything
145 */
146 private static boolean debug = false;
147 static {
148 try {
149 debug = System.getProperty("jxpath.debug") != null;
150 }
151 catch (SecurityException se) { //NOPMD
152 // This is ok
153 }
154 }
155
156 /**
157 * Private implementation method - will find the implementation
158 * class in the specified order.
159 * @param property Property name
160 * @param defaultFactory Default implementation, if nothing else is found
161 *
162 * @return class name of the JXPathContextFactory
163 */
164 private static String findFactory(String property, String defaultFactory) {
165 // Use the factory ID system property first
166 try {
167 String systemProp = System.getProperty(property);
168 if (systemProp != null) {
169 if (debug) {
170 System.err.println(
171 "JXPath: found system property" + systemProp);
172 }
173 return systemProp;
174 }
175
176 }
177 catch (SecurityException se) { //NOPMD
178 // Ignore
179 }
180
181 // try to read from $java.home/lib/xml.properties
182 try {
183 String javah = System.getProperty("java.home");
184 String configFile =
185 javah
186 + File.separator
187 + "lib"
188 + File.separator
189 + "jxpath.properties";
190 File f = new File(configFile);
191 if (f.exists()) {
192 Properties props = new Properties();
193 FileInputStream fis = new FileInputStream(f);
194 try {
195 props.load(fis);
196 }
197 finally {
198 if (fis != null) {
199 try {
200 fis.close();
201 }
202 catch (IOException e) { //NOPMD
203 //swallow
204 }
205 }
206 }
207 String factory = props.getProperty(property);
208 if (factory != null) {
209 if (debug) {
210 System.err.println(
211 "JXPath: found java.home property " + factory);
212 }
213 return factory;
214 }
215 }
216 }
217 catch (IOException ex) {
218 if (debug) {
219 ex.printStackTrace();
220 }
221 }
222
223 String serviceId = "META-INF/services/" + property;
224 // try to find services in CLASSPATH
225 try {
226 ClassLoader cl = JXPathContextFactory.class.getClassLoader();
227 InputStream is = null;
228 if (cl == null) {
229 is = ClassLoader.getSystemResourceAsStream(serviceId);
230 }
231 else {
232 is = cl.getResourceAsStream(serviceId);
233 }
234
235 if (is != null) {
236 if (debug) {
237 System.err.println("JXPath: found " + serviceId);
238 }
239 BufferedReader rd =
240 new BufferedReader(new InputStreamReader(is));
241
242 String factory = null;
243 try {
244 factory = rd.readLine();
245 }
246 finally {
247 try {
248 rd.close();
249 }
250 catch (IOException e) { //NOPMD
251 //swallow
252 }
253 }
254
255 if (factory != null && !"".equals(factory)) {
256 if (debug) {
257 System.err.println(
258 "JXPath: loaded from services: " + factory);
259 }
260 return factory;
261 }
262 }
263 }
264 catch (Exception ex) {
265 if (debug) {
266 ex.printStackTrace();
267 }
268 }
269 return defaultFactory;
270 }
271 }