001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.metaboss.naming.component;
016:
017: import java.io.File;
018: import java.io.FileFilter;
019: import java.io.IOException;
020: import java.net.URL;
021: import java.net.URLClassLoader;
022: import java.util.ArrayList;
023: import java.util.HashMap;
024: import java.util.Map;
025: import java.util.StringTokenizer;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029:
030: /** This utility class loads the implementation jar */
031: class componentImplementationLoader {
032: private static final Log sLogger = LogFactory
033: .getLog(componentImplementationLoader.class);
034: // This is the storage for the dynamically loaded implementations.
035: // It keeps Class loaders loaded with implementation jars. Key is implementation package name
036: private static Map sDynamicallyLoadedImplementations = new HashMap();
037: // Loaded at first attempt. The list of directories to have a look for the dynamic implementation
038: private static String[] sComponentImplementationPath = null;
039:
040: public static ClassLoader getComponentImplementationClassLoader(
041: String pComponentImplementationFactoryClassFullName) {
042: // Look up if we have this factory's package loaded
043: int lLastDotPosition = pComponentImplementationFactoryClassFullName
044: .lastIndexOf('.');
045: // Very unlikely, but if factory does not have a package - it can not be dynamically loaded
046: if (lLastDotPosition <= 0) {
047: sLogger
048: .debug("Class loader for "
049: + pComponentImplementationFactoryClassFullName
050: + " is not found. Dynamic loading is impossible because this factory class is at the top level (not in package).");
051: return null;
052: }
053: // Obtain the loader for the package or parent packages
054: String lComponentImplementationPackageName = pComponentImplementationFactoryClassFullName
055: .substring(0, lLastDotPosition);
056: ClassLoader lClassLoader = getClassloaderForThePackage(lComponentImplementationPackageName);
057: return lClassLoader;
058: }
059:
060: // Helper. Finds class loader for the package on the component implementation path
061: // Will attempt to find a directory for full package name, if not successfull
062: private static ClassLoader getClassloaderForThePackage(
063: String pPackageName) {
064: // Obtain the loader. Use following strategy
065: // 1. See if we have loaded the class loader before for this implementation package already and return it if we have
066: // 2. See if jar with the package name.jar exists, load it in the dynamic jar loader and try step 1 once more
067: ClassLoader lClassLoader = (ClassLoader) sDynamicallyLoadedImplementations
068: .get(pPackageName);
069: if (lClassLoader == null) {
070: synchronized (sDynamicallyLoadedImplementations) {
071: lClassLoader = (ClassLoader) sDynamicallyLoadedImplementations
072: .get(pPackageName);
073: if (lClassLoader == null) {
074: // See if we can load the implementation jar from component implementation path
075: String[] lComponentImplementationPath = getComponentImplementationPath();
076: for (int i = 0; i < lComponentImplementationPath.length; i++) {
077: File lDesiredDirectory = new File(
078: lComponentImplementationPath[i]
079: + File.separator + pPackageName);
080: if (lDesiredDirectory.exists()
081: && lDesiredDirectory.isDirectory()) {
082: // Get all jar files stored in the directory
083: File[] lAllJars = lDesiredDirectory
084: .listFiles(new FileFilter() {
085: public boolean accept(File pFile) {
086: return pFile.isFile()
087: && pFile
088: .getName()
089: .toLowerCase()
090: .endsWith(
091: ".jar");
092: }
093: });
094: if (lAllJars != null && lAllJars.length > 0) {
095: try {
096: // Convert files to URL
097: URL[] lURLs = new URL[lAllJars.length];
098: for (int j = 0; j < lAllJars.length; j++)
099: lURLs[j] = lAllJars[j]
100: .getCanonicalFile()
101: .toURL();
102: sDynamicallyLoadedImplementations
103: .put(
104: pPackageName,
105: lClassLoader = new URLClassLoader(
106: lURLs,
107: componentImplementationLoader.class
108: .getClassLoader()));
109: break;
110: } catch (IOException e) {
111: sLogger
112: .error(
113: "Error loading "
114: + pPackageName
115: + " implementation archive(s) from "
116: + lDesiredDirectory
117: .getAbsolutePath(),
118: e);
119: }
120: }
121: }
122: }
123: }
124: }
125: }
126: // Do something else if classloader is still not found
127: if (lClassLoader == null) {
128: // Stil nothing found. We will now attempt to chop off last element of the package and
129: // find implementation directories for the higher package names. If one exists, we will load it and
130: // test if we can possibly find desired factory in there
131: int lLastDotPosition = pPackageName.lastIndexOf('.');
132: // If there is still something to chop off call this method recursively
133: if (lLastDotPosition > 0)
134: return getClassloaderForThePackage(pPackageName
135: .substring(0, lLastDotPosition));
136: }
137: return lClassLoader;
138: }
139:
140: // Helper. Returns path of the component implementations. Most prioritable element is at zero index.
141: private static String[] getComponentImplementationPath() {
142: if (sComponentImplementationPath == null) {
143: ArrayList lPathList = new ArrayList();
144: // See if we can load the implementation jar from component implementation path
145: // Only able to use dynamic loading if component imlementation path property is set
146: String lComponentImplementationPath = System
147: .getProperty("com.metaboss.naming.component.ComponentImplPath");
148: if (lComponentImplementationPath != null
149: && lComponentImplementationPath.length() > 0) {
150: StringTokenizer lPathTokenizer = new StringTokenizer(
151: lComponentImplementationPath, ";", false);
152: while (lPathTokenizer.hasMoreTokens())
153: lPathList.add(lPathTokenizer.nextToken());
154: }
155: sComponentImplementationPath = (String[]) lPathList
156: .toArray(new String[lPathList.size()]);
157: if (sComponentImplementationPath.length == 0)
158: sLogger
159: .debug("Dynamic loading is switched off because component implementation path is unspecified ('com.metaboss.naming.component.ComponentImplPath' system property is not present or empty).");
160: }
161: return sComponentImplementationPath;
162: }
163: }
|