View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.discovery.tools;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.Properties;
22  
23  import org.apache.commons.discovery.DiscoveryException;
24  import org.apache.commons.discovery.Resource;
25  import org.apache.commons.discovery.ResourceIterator;
26  import org.apache.commons.discovery.resource.ClassLoaders;
27  import org.apache.commons.discovery.resource.DiscoverResources;
28  
29  /**
30   * Mechanisms to locate and load a class.
31   *
32   * The load methods locate a class only.
33   * The find methods locate a class and verify that the
34   * class implements an given interface or extends a given class.
35   */
36  public class ResourceUtils {
37  
38      /**
39       * Get package name.
40       *
41       * Not all class loaders 'keep' package information,
42       * in which case Class.getPackage() returns null.
43       * This means that calling Class.getPackage().getName()
44       * is unreliable at best.
45       *
46       * @param clazz The class from which the package has to be extracted
47       * @return The string representation of the input class package
48       */
49      public static String getPackageName(Class<?> clazz) {
50          Package clazzPackage = clazz.getPackage();
51          String packageName;
52          if (clazzPackage != null) {
53              packageName = clazzPackage.getName();
54          } else {
55              String clazzName = clazz.getName();
56              packageName = new String(clazzName.toCharArray(), 0, clazzName.lastIndexOf('.'));
57          }
58          return packageName;
59      }
60  
61      /**
62       * Load the resource <code>resourceName</code>.
63       *
64       * Try each classloader in succession,
65       * until first succeeds, or all fail.
66       * If all fail and <code>resouceName</code> is not absolute
67       * (doesn't start with '/' character), then retry with
68       * <code>packageName/resourceName</code> after changing all
69       * '.' to '/'.
70       *
71       * @param spi The SPI type
72       * @param resourceName The name of the resource to load.
73       * @param loaders the class loaders holder
74       * @return The discovered {@link Resource} instance
75       * @throws DiscoveryException if the class implementing
76       *            the SPI cannot be found, cannot be loaded and
77       *            instantiated, or if the resulting class does not implement
78       *            (or extend) the SPI
79       */
80      public static Resource getResource(Class<?> spi,
81                                         String resourceName,
82                                         ClassLoaders loaders) throws DiscoveryException {
83          DiscoverResources explorer = new DiscoverResources(loaders);
84          ResourceIterator resources = explorer.findResources(resourceName);
85  
86          if (spi != null  &&
87              !resources.hasNext()  &&
88              resourceName.charAt(0) != '/') {
89              /**
90               * If we didn't find the resource, and if the resourceName
91               * isn't an 'absolute' path name, then qualify with
92               * package name of the spi.
93               */
94              resourceName = getPackageName(spi).replace('.','/') + "/" + resourceName;
95              resources = explorer.findResources(resourceName);
96          }
97  
98          return resources.hasNext()
99                 ? resources.nextResource()
100                : null;
101     }
102 
103     /**
104      * Load named property file, optionally qualified by spi's package name
105      * as per Class.getResource.
106      *
107      * A property file is loaded using the following sequence of class loaders:
108      *   <ul>
109      *     <li>Thread Context Class Loader</li>
110      *     <li>DiscoverSingleton's Caller's Class Loader</li>
111      *     <li>SPI's Class Loader</li>
112      *     <li>DiscoverSingleton's (this class) Class Loader</li>
113      *     <li>System Class Loader</li>
114      *   </ul>
115      *
116      * @param spi The SPI type
117      * @param propertiesFileName The property file name.
118      * @param classLoaders The class loaders holder
119      * @return The loaded named property file, in {@code Properties} format
120      * @throws DiscoveryException Thrown if the name of a class implementing
121      *            the SPI cannot be found, if the class cannot be loaded and
122      *            instantiated, or if the resulting class does not implement
123      *            (or extend) the SPI.
124      */
125     public static Properties loadProperties(Class<?> spi,
126                                             String propertiesFileName,
127                                             ClassLoaders classLoaders) throws DiscoveryException {
128         Properties properties = null;
129 
130         if (propertiesFileName != null) {
131             try {
132                 Resource resource = getResource(spi, propertiesFileName, classLoaders);
133                 if (resource != null) {
134                     InputStream stream = resource.getResourceAsStream();
135 
136                     if (stream != null) {
137                         properties = new Properties();
138                         try {
139                             properties.load(stream);
140                         } finally {
141                             stream.close();
142                         }
143                     }
144                 }
145             } catch (IOException e) {
146                 // ignore
147             } catch (SecurityException e) {
148                 // ignore
149             }
150         }
151 
152         return properties;
153     }
154 
155 }