1. /*
  2. * Copyright 2001-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.logging.impl;
  17. import java.lang.reflect.Constructor;
  18. import java.lang.reflect.InvocationTargetException;
  19. import java.lang.reflect.Method;
  20. import java.security.AccessController;
  21. import java.security.PrivilegedAction;
  22. import java.util.Enumeration;
  23. import java.util.Hashtable;
  24. import java.util.Vector;
  25. import org.apache.commons.logging.Log;
  26. import org.apache.commons.logging.LogConfigurationException;
  27. import org.apache.commons.logging.LogFactory;
  28. /**
  29. * <p>Concrete subclass of {@link LogFactory} that implements the
  30. * following algorithm to dynamically select a logging implementation
  31. * class to instantiate a wrapper for.</p>
  32. * <ul>
  33. * <li>Use a factory configuration attribute named
  34. * <code>org.apache.commons.logging.Log</code> to identify the
  35. * requested implementation class.</li>
  36. * <li>Use the <code>org.apache.commons.logging.Log</code> system property
  37. * to identify the requested implementation class.</li>
  38. * <li>If <em>Log4J</em> is available, return an instance of
  39. * <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
  40. * <li>If <em>JDK 1.4 or later</em> is available, return an instance of
  41. * <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
  42. * <li>Otherwise, return an instance of
  43. * <code>org.apache.commons.logging.impl.SimpleLog</code>.</li>
  44. * </ul>
  45. *
  46. * <p>If the selected {@link Log} implementation class has a
  47. * <code>setLogFactory()</code> method that accepts a {@link LogFactory}
  48. * parameter, this method will be called on each newly created instance
  49. * to identify the associated factory. This makes factory configuration
  50. * attributes available to the Log instance, if it so desires.</p>
  51. *
  52. * <p>This factory will remember previously created <code>Log</code> instances
  53. * for the same name, and will return them on repeated requests to the
  54. * <code>getInstance()</code> method. This implementation ignores any
  55. * configured attributes.</p>
  56. *
  57. * @author Rod Waldhoff
  58. * @author Craig R. McClanahan
  59. * @author Richard A. Sitze
  60. * @version $Revision: 1.33 $ $Date: 2004/03/06 21:52:59 $
  61. */
  62. public class LogFactoryImpl extends LogFactory {
  63. // ----------------------------------------------------------- Constructors
  64. /**
  65. * Public no-arguments constructor required by the lookup mechanism.
  66. */
  67. public LogFactoryImpl() {
  68. super();
  69. }
  70. // ----------------------------------------------------- Manifest Constants
  71. /**
  72. * The name of the system property identifying our {@link Log}
  73. * implementation class.
  74. */
  75. public static final String LOG_PROPERTY =
  76. "org.apache.commons.logging.Log";
  77. /**
  78. * The deprecated system property used for backwards compatibility with
  79. * the old {@link org.apache.commons.logging.LogSource} class.
  80. */
  81. protected static final String LOG_PROPERTY_OLD =
  82. "org.apache.commons.logging.log";
  83. /**
  84. * <p>The name of the {@link Log} interface class.</p>
  85. */
  86. private static final String LOG_INTERFACE =
  87. "org.apache.commons.logging.Log";
  88. // ----------------------------------------------------- Instance Variables
  89. /**
  90. * Configuration attributes.
  91. */
  92. protected Hashtable attributes = new Hashtable();
  93. /**
  94. * The {@link org.apache.commons.logging.Log} instances that have
  95. * already been created, keyed by logger name.
  96. */
  97. protected Hashtable instances = new Hashtable();
  98. /**
  99. * Name of the class implementing the Log interface.
  100. */
  101. private String logClassName;
  102. /**
  103. * The one-argument constructor of the
  104. * {@link org.apache.commons.logging.Log}
  105. * implementation class that will be used to create new instances.
  106. * This value is initialized by <code>getLogConstructor()</code>,
  107. * and then returned repeatedly.
  108. */
  109. protected Constructor logConstructor = null;
  110. /**
  111. * The signature of the Constructor to be used.
  112. */
  113. protected Class logConstructorSignature[] =
  114. { java.lang.String.class };
  115. /**
  116. * The one-argument <code>setLogFactory</code> method of the selected
  117. * {@link org.apache.commons.logging.Log} method, if it exists.
  118. */
  119. protected Method logMethod = null;
  120. /**
  121. * The signature of the <code>setLogFactory</code> method to be used.
  122. */
  123. protected Class logMethodSignature[] =
  124. { LogFactory.class };
  125. // --------------------------------------------------------- Public Methods
  126. /**
  127. * Return the configuration attribute with the specified name (if any),
  128. * or <code>null</code> if there is no such attribute.
  129. *
  130. * @param name Name of the attribute to return
  131. */
  132. public Object getAttribute(String name) {
  133. return (attributes.get(name));
  134. }
  135. /**
  136. * Return an array containing the names of all currently defined
  137. * configuration attributes. If there are no such attributes, a zero
  138. * length array is returned.
  139. */
  140. public String[] getAttributeNames() {
  141. Vector names = new Vector();
  142. Enumeration keys = attributes.keys();
  143. while (keys.hasMoreElements()) {
  144. names.addElement((String) keys.nextElement());
  145. }
  146. String results[] = new String[names.size()];
  147. for (int i = 0; i < results.length; i++) {
  148. results[i] = (String) names.elementAt(i);
  149. }
  150. return (results);
  151. }
  152. /**
  153. * Convenience method to derive a name from the specified class and
  154. * call <code>getInstance(String)</code> with it.
  155. *
  156. * @param clazz Class for which a suitable Log name will be derived
  157. *
  158. * @exception LogConfigurationException if a suitable <code>Log</code>
  159. * instance cannot be returned
  160. */
  161. public Log getInstance(Class clazz) throws LogConfigurationException {
  162. return (getInstance(clazz.getName()));
  163. }
  164. /**
  165. * <p>Construct (if necessary) and return a <code>Log</code> instance,
  166. * using the factory's current set of configuration attributes.</p>
  167. *
  168. * <p><strong>NOTE</strong> - Depending upon the implementation of
  169. * the <code>LogFactory</code> you are using, the <code>Log</code>
  170. * instance you are returned may or may not be local to the current
  171. * application, and may or may not be returned again on a subsequent
  172. * call with the same name argument.</p>
  173. *
  174. * @param name Logical name of the <code>Log</code> instance to be
  175. * returned (the meaning of this name is only known to the underlying
  176. * logging implementation that is being wrapped)
  177. *
  178. * @exception LogConfigurationException if a suitable <code>Log</code>
  179. * instance cannot be returned
  180. */
  181. public Log getInstance(String name) throws LogConfigurationException {
  182. Log instance = (Log) instances.get(name);
  183. if (instance == null) {
  184. instance = newInstance(name);
  185. instances.put(name, instance);
  186. }
  187. return (instance);
  188. }
  189. /**
  190. * Release any internal references to previously created
  191. * {@link org.apache.commons.logging.Log}
  192. * instances returned by this factory. This is useful in environments
  193. * like servlet containers, which implement application reloading by
  194. * throwing away a ClassLoader. Dangling references to objects in that
  195. * class loader would prevent garbage collection.
  196. */
  197. public void release() {
  198. instances.clear();
  199. }
  200. /**
  201. * Remove any configuration attribute associated with the specified name.
  202. * If there is no such attribute, no action is taken.
  203. *
  204. * @param name Name of the attribute to remove
  205. */
  206. public void removeAttribute(String name) {
  207. attributes.remove(name);
  208. }
  209. /**
  210. * Set the configuration attribute with the specified name. Calling
  211. * this with a <code>null</code> value is equivalent to calling
  212. * <code>removeAttribute(name)</code>.
  213. *
  214. * @param name Name of the attribute to set
  215. * @param value Value of the attribute to set, or <code>null</code>
  216. * to remove any setting for this attribute
  217. */
  218. public void setAttribute(String name, Object value) {
  219. if (value == null) {
  220. attributes.remove(name);
  221. } else {
  222. attributes.put(name, value);
  223. }
  224. }
  225. // ------------------------------------------------------ Protected Methods
  226. /**
  227. * Return the fully qualified Java classname of the {@link Log}
  228. * implementation we will be using.
  229. */
  230. protected String getLogClassName() {
  231. // Return the previously identified class name (if any)
  232. if (logClassName != null) {
  233. return logClassName;
  234. }
  235. logClassName = (String) getAttribute(LOG_PROPERTY);
  236. if (logClassName == null) { // @deprecated
  237. logClassName = (String) getAttribute(LOG_PROPERTY_OLD);
  238. }
  239. if (logClassName == null) {
  240. try {
  241. logClassName = System.getProperty(LOG_PROPERTY);
  242. } catch (SecurityException e) {
  243. ;
  244. }
  245. }
  246. if (logClassName == null) { // @deprecated
  247. try {
  248. logClassName = System.getProperty(LOG_PROPERTY_OLD);
  249. } catch (SecurityException e) {
  250. ;
  251. }
  252. }
  253. if ((logClassName == null) && isLog4JAvailable()) {
  254. logClassName = "org.apache.commons.logging.impl.Log4JLogger";
  255. }
  256. if ((logClassName == null) && isJdk14Available()) {
  257. logClassName = "org.apache.commons.logging.impl.Jdk14Logger";
  258. }
  259. if ((logClassName == null) && isJdk13LumberjackAvailable()) {
  260. logClassName = "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
  261. }
  262. if (logClassName == null) {
  263. logClassName = "org.apache.commons.logging.impl.SimpleLog";
  264. }
  265. return (logClassName);
  266. }
  267. /**
  268. * <p>Return the <code>Constructor</code> that can be called to instantiate
  269. * new {@link org.apache.commons.logging.Log} instances.</p>
  270. *
  271. * <p><strong>IMPLEMENTATION NOTE</strong> - Race conditions caused by
  272. * calling this method from more than one thread are ignored, because
  273. * the same <code>Constructor</code> instance will ultimately be derived
  274. * in all circumstances.</p>
  275. *
  276. * @exception LogConfigurationException if a suitable constructor
  277. * cannot be returned
  278. */
  279. protected Constructor getLogConstructor()
  280. throws LogConfigurationException {
  281. // Return the previously identified Constructor (if any)
  282. if (logConstructor != null) {
  283. return logConstructor;
  284. }
  285. String logClassName = getLogClassName();
  286. // Attempt to load the Log implementation class
  287. Class logClass = null;
  288. Class logInterface = null;
  289. try {
  290. logInterface = this.getClass().getClassLoader().loadClass
  291. (LOG_INTERFACE);
  292. logClass = loadClass(logClassName);
  293. if (logClass == null) {
  294. throw new LogConfigurationException
  295. ("No suitable Log implementation for " + logClassName);
  296. }
  297. if (!logInterface.isAssignableFrom(logClass)) {
  298. Class interfaces[] = logClass.getInterfaces();
  299. for (int i = 0; i < interfaces.length; i++) {
  300. if (LOG_INTERFACE.equals(interfaces[i].getName())) {
  301. throw new LogConfigurationException
  302. ("Invalid class loader hierarchy. " +
  303. "You have more than one version of '" +
  304. LOG_INTERFACE + "' visible, which is " +
  305. "not allowed.");
  306. }
  307. }
  308. throw new LogConfigurationException
  309. ("Class " + logClassName + " does not implement '" +
  310. LOG_INTERFACE + "'.");
  311. }
  312. } catch (Throwable t) {
  313. throw new LogConfigurationException(t);
  314. }
  315. // Identify the <code>setLogFactory</code> method (if there is one)
  316. try {
  317. logMethod = logClass.getMethod("setLogFactory",
  318. logMethodSignature);
  319. } catch (Throwable t) {
  320. logMethod = null;
  321. }
  322. // Identify the corresponding constructor to be used
  323. try {
  324. logConstructor = logClass.getConstructor(logConstructorSignature);
  325. return (logConstructor);
  326. } catch (Throwable t) {
  327. throw new LogConfigurationException
  328. ("No suitable Log constructor " +
  329. logConstructorSignature+ " for " + logClassName, t);
  330. }
  331. }
  332. /**
  333. * MUST KEEP THIS METHOD PRIVATE.
  334. *
  335. * <p>Exposing this method outside of
  336. * <code>org.apache.commons.logging.LogFactoryImpl</code>
  337. * will create a security violation:
  338. * This method uses <code>AccessController.doPrivileged()</code>.
  339. * </p>
  340. *
  341. * Load a class, try first the thread class loader, and
  342. * if it fails use the loader that loaded this class.
  343. */
  344. private static Class loadClass( final String name )
  345. throws ClassNotFoundException
  346. {
  347. Object result = AccessController.doPrivileged(
  348. new PrivilegedAction() {
  349. public Object run() {
  350. ClassLoader threadCL = getContextClassLoader();
  351. if (threadCL != null) {
  352. try {
  353. return threadCL.loadClass(name);
  354. } catch( ClassNotFoundException ex ) {
  355. // ignore
  356. }
  357. }
  358. try {
  359. return Class.forName( name );
  360. } catch (ClassNotFoundException e) {
  361. return e;
  362. }
  363. }
  364. });
  365. if (result instanceof Class)
  366. return (Class)result;
  367. throw (ClassNotFoundException)result;
  368. }
  369. /**
  370. * Is <em>JDK 1.3 with Lumberjack</em> logging available?
  371. */
  372. protected boolean isJdk13LumberjackAvailable() {
  373. try {
  374. loadClass("java.util.logging.Logger");
  375. loadClass("org.apache.commons.logging.impl.Jdk13LumberjackLogger");
  376. return (true);
  377. } catch (Throwable t) {
  378. return (false);
  379. }
  380. }
  381. /**
  382. * <p>Return <code>true</code> if <em>JDK 1.4 or later</em> logging
  383. * is available. Also checks that the <code>Throwable</code> class
  384. * supports <code>getStackTrace()</code>, which is required by
  385. * Jdk14Logger.</p>
  386. */
  387. protected boolean isJdk14Available() {
  388. try {
  389. loadClass("java.util.logging.Logger");
  390. loadClass("org.apache.commons.logging.impl.Jdk14Logger");
  391. Class throwable = loadClass("java.lang.Throwable");
  392. if (throwable.getDeclaredMethod("getStackTrace", null) == null) {
  393. return (false);
  394. }
  395. return (true);
  396. } catch (Throwable t) {
  397. return (false);
  398. }
  399. }
  400. /**
  401. * Is a <em>Log4J</em> implementation available?
  402. */
  403. protected boolean isLog4JAvailable() {
  404. try {
  405. loadClass("org.apache.log4j.Logger");
  406. loadClass("org.apache.commons.logging.impl.Log4JLogger");
  407. return (true);
  408. } catch (Throwable t) {
  409. return (false);
  410. }
  411. }
  412. /**
  413. * Create and return a new {@link org.apache.commons.logging.Log}
  414. * instance for the specified name.
  415. *
  416. * @param name Name of the new logger
  417. *
  418. * @exception LogConfigurationException if a new instance cannot
  419. * be created
  420. */
  421. protected Log newInstance(String name) throws LogConfigurationException {
  422. Log instance = null;
  423. try {
  424. Object params[] = new Object[1];
  425. params[0] = name;
  426. instance = (Log) getLogConstructor().newInstance(params);
  427. if (logMethod != null) {
  428. params[0] = this;
  429. logMethod.invoke(instance, params);
  430. }
  431. return (instance);
  432. } catch (InvocationTargetException e) {
  433. Throwable c = e.getTargetException();
  434. if (c != null) {
  435. throw new LogConfigurationException(c);
  436. } else {
  437. throw new LogConfigurationException(e);
  438. }
  439. } catch (Throwable t) {
  440. throw new LogConfigurationException(t);
  441. }
  442. }
  443. }