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
018package org.apache.log4j.helpers;
019
020import java.net.URL;
021import java.lang.reflect.Method;
022import java.lang.reflect.InvocationTargetException;
023import java.io.InterruptedIOException;
024
025
026/**
027   Load resources (or images) from various sources.
028 
029  @author Ceki Gülcü
030 */
031
032public class Loader  { 
033
034  static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
035
036  // We conservatively assume that we are running under Java 1.x
037  static private boolean java1 = true;
038  
039  static private boolean ignoreTCL = false;
040  
041  static {
042    String prop = OptionConverter.getSystemProperty("java.version", null);
043    
044    if(prop != null) {
045      int i = prop.indexOf('.');
046      if(i != -1) {     
047        if(prop.charAt(i+1) != '1')
048          java1 = false;
049      } 
050    }
051    String ignoreTCLProp = OptionConverter.getSystemProperty("log4j.ignoreTCL", null);
052    if(ignoreTCLProp != null) {
053      ignoreTCL = OptionConverter.toBoolean(ignoreTCLProp, true);      
054    }   
055  }
056  
057  /**
058   *  Get a resource by delegating to getResource(String).
059   *  @param resource resource name
060   *  @param clazz class, ignored.
061   *  @return URL to resource or null.
062   *  @deprecated as of 1.2.
063   */
064  public static URL getResource(String resource, Class clazz) {
065      return getResource(resource);
066  }
067
068  /**
069     This method will search for <code>resource</code> in different
070     places. The search order is as follows:
071
072     <ol>
073
074     <p><li>Search for <code>resource</code> using the thread context
075     class loader under Java2. If that fails, search for
076     <code>resource</code> using the class loader that loaded this
077     class (<code>Loader</code>). Under JDK 1.1, only the the class
078     loader that loaded this class (<code>Loader</code>) is used.
079
080     <p><li>Try one last time with
081     <code>ClassLoader.getSystemResource(resource)</code>, that is is
082     using the system class loader in JDK 1.2 and virtual machine's
083     built-in class loader in JDK 1.1.
084
085     </ol>
086  */
087  static public URL getResource(String resource) {
088    ClassLoader classLoader = null;
089    URL url = null;
090    
091    try {
092        if(!java1 && !ignoreTCL) {
093          classLoader = getTCL();
094          if(classLoader != null) {
095            LogLog.debug("Trying to find ["+resource+"] using context classloader "
096                         +classLoader+".");
097            url = classLoader.getResource(resource);      
098            if(url != null) {
099              return url;
100            }
101          }
102        }
103        
104        // We could not find resource. Ler us now try with the
105        // classloader that loaded this class.
106        classLoader = Loader.class.getClassLoader(); 
107        if(classLoader != null) {
108          LogLog.debug("Trying to find ["+resource+"] using "+classLoader
109                       +" class loader.");
110          url = classLoader.getResource(resource);
111          if(url != null) {
112            return url;
113          }
114        }
115    } catch(IllegalAccessException t) {
116        LogLog.warn(TSTR, t);
117    } catch(InvocationTargetException t) {
118        if (t.getTargetException() instanceof InterruptedException
119                || t.getTargetException() instanceof InterruptedIOException) {
120            Thread.currentThread().interrupt();
121        }
122        LogLog.warn(TSTR, t);
123    } catch(Throwable t) {
124      //
125      //  can't be InterruptedException or InterruptedIOException
126      //    since not declared, must be error or RuntimeError.
127      LogLog.warn(TSTR, t);
128    }
129    
130    // Last ditch attempt: get the resource from the class path. It
131    // may be the case that clazz was loaded by the Extentsion class
132    // loader which the parent of the system class loader. Hence the
133    // code below.
134    LogLog.debug("Trying to find ["+resource+
135                   "] using ClassLoader.getSystemResource().");
136    return ClassLoader.getSystemResource(resource);
137  } 
138  
139  /**
140     Are we running under JDK 1.x?        
141  */
142  public
143  static
144  boolean isJava1() {
145    return java1;
146  }
147  
148  /**
149    * Get the Thread Context Loader which is a JDK 1.2 feature. If we
150    * are running under JDK 1.1 or anything else goes wrong the method
151    * returns <code>null<code>.
152    *
153    *  */
154  private static ClassLoader getTCL() throws IllegalAccessException, 
155    InvocationTargetException {
156
157    // Are we running on a JDK 1.2 or later system?
158    Method method = null;
159    try {
160      method = Thread.class.getMethod("getContextClassLoader", null);
161    } catch (NoSuchMethodException e) {
162      // We are running on JDK 1.1
163      return null;
164    }
165    
166    return (ClassLoader) method.invoke(Thread.currentThread(), null);
167  }
168
169
170  
171  /**
172   * If running under JDK 1.2 load the specified class using the
173   *  <code>Thread</code> <code>contextClassLoader</code> if that
174   *  fails try Class.forname. Under JDK 1.1 only Class.forName is
175   *  used.
176   *
177   */
178  static public Class loadClass (String clazz) throws ClassNotFoundException {
179    // Just call Class.forName(clazz) if we are running under JDK 1.1
180    // or if we are instructed to ignore the TCL.
181    if(java1 || ignoreTCL) {
182      return Class.forName(clazz);
183    } else {
184      try {
185            return getTCL().loadClass(clazz);
186      }
187      // we reached here because tcl was null or because of a
188      // security exception, or because clazz could not be loaded...
189      // In any case we now try one more time
190      catch(InvocationTargetException e) {
191          if (e.getTargetException() instanceof InterruptedException
192                  || e.getTargetException() instanceof InterruptedIOException) {
193              Thread.currentThread().interrupt();
194          }
195      } catch(Throwable t) {
196      }
197    }
198    return Class.forName(clazz);
199  }
200}