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 */ 017package org.apache.commons.jxpath; 018 019import java.io.BufferedReader; 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.InputStreamReader; 025import java.util.Properties; 026 027import org.apache.commons.jxpath.util.ClassLoaderUtil; 028 029/** 030 * Defines a factory API that enables applications to obtain a 031 * {@link JXPathContext} instance. To acquire a JXPathContext, first call the 032 * static {@link #newInstance} method of JXPathContextFactory. 033 * This method returns a concrete JXPathContextFactory. 034 * Then call {@link #newContext} on that instance. You will rarely 035 * need to perform these steps explicitly: usually you can call one of the 036 * <code>JXPathContex.newContext</code> methods, which will perform these steps 037 * for you. 038 * 039 * @see JXPathContext#newContext(Object) 040 * @see JXPathContext#newContext(JXPathContext,Object) 041 * 042 * @author Dmitri Plotnikov 043 * @version $Revision: 1234255 $ $Date: 2012-01-21 04:11:46 +0100 (Sa, 21 Jan 2012) $ 044 */ 045public abstract class JXPathContextFactory { 046 047 /** The default property */ 048 public static final String FACTORY_NAME_PROPERTY = 049 "org.apache.commons.jxpath.JXPathContextFactory"; 050 051 /** The default factory class */ 052 private static final String DEFAULT_FACTORY_CLASS = 053 "org.apache.commons.jxpath.ri.JXPathContextFactoryReferenceImpl"; 054 055 /** Avoid reading all the files when the findFactory 056 method is called the second time ( cache the result of 057 finding the default impl ) 058 */ 059 private static String factoryImplName = null; 060 061 /** 062 * Create a new JXPathContextFactory. 063 */ 064 protected JXPathContextFactory () { 065 066 } 067 068 /** 069 * Obtain a new instance of a <code>JXPathContextFactory</code>. 070 * This static method creates a new factory instance. 071 * This method uses the following ordered lookup procedure to determine 072 * the <code>JXPathContextFactory</code> implementation class to load: 073 * <ul> 074 * <li> 075 * Use the <code>org.apache.commons.jxpath.JXPathContextFactory</code> 076 * system property. 077 * </li> 078 * <li> 079 * Alternatively, use the JAVA_HOME (the parent directory where jdk is 080 * installed)/lib/jxpath.properties for a property file that contains the 081 * name of the implementation class keyed on 082 * <code>org.apache.commons.jxpath.JXPathContextFactory</code>. 083 * </li> 084 * <li> 085 * Use the Services API (as detailed in the JAR specification), if 086 * available, to determine the classname. The Services API will look 087 * for a classname in the file 088 * <code>META- INF/services/<i>org.apache.commons.jxpath. 089 * JXPathContextFactory</i></code> in jars available to the runtime. 090 * </li> 091 * <li> 092 * Platform default <code>JXPathContextFactory</code> instance. 093 * </li> 094 * </ul> 095 * 096 * Once an application has obtained a reference to a 097 * <code>JXPathContextFactory</code> it can use the factory to 098 * obtain JXPathContext instances. 099 * 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}