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