Tomcat: Accessing web app classes from shared libraries
February 06, 2021 —
Vinayak Gadkari
It is not a recommended practise to have libraries placed in Tomcat’s lib directory to access classes in a web application. However, there are cases when you may want to flout this practise. Tomcat’s class loading mechanism will not allow you to do so since the web classes and the lib classes are loaded by different class loaders. The workaround in such a case is to use a custom bootstrap loader. This loader should be used to load all the application specific classes.
An example of a custom bootstrap loader is given below. The custom class loader can load classes from path tokens separated with a semicolon (similar to the Java class path). The path tokens can specified in one of the following forms:
- Absolute path (c:\mytomcat\lib\myjar.jar)
- Absolute path ending with a wildcard (c:\mytomcat\lib*)
- Path using the ROOTDIR env or JVM property (%ROOTDIR%\lib\myjar.jar)
- Path using the ROOTDIR env or JVM property and ending with a wildcard (%ROOTDIR%\lib*)
BootstrapLoader.java
package test.tomcat;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class BootstrapLoader
{
public BootstrapLoader(String path)
{
URL[] class_path = getClassPath(path);
setClassLoader(class_path);
}
private URL[] getClassPath(String path)
{
String real_path = Utility.replaceRootDir(path);
StringTokenizer token = new StringTokenizer(real_path, delim);
List<URL> class_path = new ArrayList<URL>();
while(token.hasMoreTokens())
{
String file_name = token.nextToken();
// Get all jars if file name ends with wild-card character
if (file_name.endsWith(wildcard_spec))
{
file_name = file_name.substring(0, file_name.length()
- wildcard_spec.length());
class_path.addAll(Utility.classPathFromJars(file_name));
}
// Else append the file name as it is
else
{
URL url = Utility.getFileURL(file_name);
if (null != url)
{
class_path.add(url);
}
}
}
return class_path.toArray(new URL[0]);
}
private void setClassLoader(URL[] class_path)
{
ClassLoader custom_cl = new URLClassLoader(class_path);
Thread.currentThread().setContextClassLoader(custom_cl);
}
public static void main(String[] args)
{
BootstrapLoader b = new BootstrapLoader("%ROOTDIR%;%ROOTDIR%\\$Realtime Framework_v5.3.02.319464_Uninstall$\\realtime\\tools\\winmon\\02-java\\src\\*");
}
private final static String delim = ";";
private final static String wildcard_spec = "/*";
}
Utility.java
package test.tomcat;
import java.io.File;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
final public class Utility
{
final private static String ROOT_ENV_VAR = "ROOTDIR";
public static String getRootDir()
{
String root_dir = System.getProperty(ROOT_ENV_VAR);
if (null == root_dir)
{
root_dir = System.getenv(ROOT_ENV_VAR);
}
if (null != root_dir)
{
root_dir = root_dir.replaceAll("\\\\", "/");
}
return root_dir;
}
// Replace back slashes and env variable
public static String replaceRootDir(String str)
{
final String root_dir = getRootDir();
str = str.replaceAll("\\\\", "/");
if (null != root_dir)
{
str = str.replaceAll("(?i)%" + ROOT_ENV_VAR + "%", root_dir);
}
return str;
}
public static URL getFileURL(String file_path)
{
URL url = null;
try
{
File file = new File(file_path);
if( file.exists() )
{
url = file.toURI().toURL();
}
}
catch(MalformedURLException ex)
{
// Log the exception and continue
// ....
}
return url;
}
public static List<URL> classPathFromJars(String dir_name)
{
final File dir = new File(dir_name);
List<URL> class_path = new ArrayList<URL>();
final FilenameFilter filter = new FilenameFilter()
{
public boolean accept(File dir, String name)
{
return name.endsWith(".jar");
}
};
final String[] jar_names = dir.list(filter);
if (null != jar_names)
{
for (String jar_name : jar_names)
{
URL url = getFileURL(dir + File.separator + jar_name);
if (null != url)
{
class_path.add(url);
}
}
}
return class_path;
}
private Utility()
{}
}