Name: rv122619 Date: 07/29/2004
When we replace the System classloader without our own using the approved java.lang.class.loader method the VM fails to start. This is caused by the UnixFileSystem.list method constructing a String. The String construction is failing trying to find the system charset before the SystemClassloader is finished being constructed. This happens on most non-english locales. Specificly JA. This happens on Solaris as well as Linux. I have not been able to try this on Windows.
Included are 3 source files and a config file. compile and run with the following script:
echo "compiling"
javac loader/src/com/jim/*.java
jar cf loader.jar -C loader/src .
mv loader.jar loader/dist
/bigdisk/suwall/jdk1.5.0/bin/javac -source 1.4 test.java
/bigdisk/suwall/jdk1.5.0/bin/jar cf test.jar *.class
export LC_ALL=ja
locale
echo ""
for x in /usr/j2re1.4.1_02/bin
do
${x}/java -version
${x}/java -debug -cp "loader/dist/loader.jar" -Dsas.app.class.dirs="." -Djava.system.class.loader=com.jim.AppClassLoader test
=com/jim/AppClassLoader.java========================================
package com.jim;
import java.io.IOException;
import java.io.File;
import java.net.URL;
import java.net.MalformedURLException;
import java.net.URLStreamHandler;
import java.util.Vector;
import java.net.URLStreamHandlerFactory;
import java.util.StringTokenizer;
import sun.net.www.ParseUtil;
public class AppClassLoader
extends java.net.URLClassLoader
{
/**
* The AppClassLoader is the start of the SAS Application level classloaders.
* This classloader defines the location for the classes used by the application.
* The system property "sas.app.class.dirs" points to a set of directories that will
* be scanned for jar files. This acts much like the extensions classloader. The property
* sas.app.class.path points to a list of classes or jars that actually define the application.
* This acts much like the classpath and are prepended to the list of jars found using the
* "sas.app.class.dirs" property.
*/
public AppClassLoader(ClassLoader parent)
{
super(getFileURLs("sas.app.class.dirs", "sas.app.class.path"), new ExtClassLoader(parent)); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* scans the System property for directories. Each directory is scanned for files.
* Each file is then returned as an URL. This has the effect of added every jar and
* zip in each directory to the list. This has the side effect of adding and sub-directory
* as a directory of classes to the list.
*/
private static URL[] getFileURLs(String jarProperty, String pathProperty)
{
// create an array of path names from the webaf.class.path property
File[] dirs = getDirs(jarProperty);
File[] path = getPath(pathProperty);
// translate these pathnames in URLs
Vector urls = new Vector();
int i;
// first do the path if it exists
for(i=0;i<path.length;++i)
{
if (path[i] != null)
urls.add(getFileURL(path[i]));
}
// next do the jar directories
for (i = 0; i < dirs.length; i++)
{
String[] files = dirs[i].list();
if (files != null)
{
for (int j = 0; j < files.length; j++)
{
File f = new File(dirs[i], files[j]);
urls.add(getFileURL(f));
}
}
}
URL[] ua = new URL[urls.size()];
urls.copyInto(ua);
return ua;
}
/**
* scans the System property for a list of directories.
*/
private static File[] getDirs(String property)
{
String s = System.getProperty(property);
File[] dirs;
if (s != null)
{
StringTokenizer st =
new StringTokenizer(s, File.pathSeparator);
int count = st.countTokens();
dirs = new File[count];
for (int i = 0; i < count; i++)
{
dirs[i] = new File(st.nextToken());
}
}
else
{
dirs = new File[0];
}
return dirs;
}
/**
* scans the System property for a list of files.
*/
private static File[] getPath(String property)
{
String s = System.getProperty(property);
File[] dirs;
if (s != null)
{
StringTokenizer st =
new StringTokenizer(s, File.pathSeparator);
int count = st.countTokens();
dirs = new File[count];
for (int i = 0; i < count; i++)
{
dirs[i] = new File(st.nextToken());
}
}
else
{
dirs = new File[0];
}
return dirs;
}
/**
* Translated the passed in File into an URL. Handles spaces in file names.
* @param file The File to be translated into an URL
* @return URL The file location translated into an URL
*/
private static URL getFileURL(File file)
{
try
{
file = file.getCanonicalFile();
}
catch (IOException e) {}
try
{
return ParseUtil.fileToEncodedURL(file);
}
catch (MalformedURLException e)
{
// Should never happen since we specify the protocol...
throw new InternalError();
}
}
/**
* Appends the specified URL to the list of URLs to search for classes and resources.
*
* @param url the URL to be added to the search path of URLs
**/
public void add(URL u)
{
addURL(u);
}
}
=com/jim/ExtClassLoader.java===================================================
package com.jim;
import java.io.IOException;
import java.io.File;
import java.net.URL;
import java.net.MalformedURLException;
import java.net.URLStreamHandler;
import java.util.Vector;
import java.net.URLStreamHandlerFactory;
import java.io.FileInputStream;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;
import java.io.FileNotFoundException;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.Locale;
public class ExtClassLoader
extends java.net.URLClassLoader
{
/*
* The ExtClassLoader scans the normal extensions classloader
* for a known set of URLs. These URLs are identified from the contents
* of a config file. The default config is com.sas.app.JavaEXTs.config.
* an alternate file will be used if it is speicified in the System property
* "sas.java.config"
*/
public ExtClassLoader(ClassLoader parent)
{
super(getFilteredURLs(parent), null); // skip the extensions classloader
}
/**
* getFileredURLs scans the extensions classloader for its known URLs and only uses
* a subset as defined by the contents of JavaEXTs.config. The config file is a list of jar
* names. Absolute names are not used. Each URL is scanned to determine if it points to
* a jar named in the config file. Any matches are selected and returned by this method.
* All jar names must be complete names. Fragments will not match.
*/
private static URL[] getFilteredURLs(ClassLoader parent)
{
ClassLoaderUtil clu = new ClassLoaderUtil();
Vector v = new Vector();
int num = 0;
// read the config file. This handles finding the config via the System Property
// first then looking near the class.
String configLocation=null;
try
{
configLocation = System.getProperty("sas.ext.config"); //$NON-NLS-1$
InputStream in=null;
if (configLocation != null)
{
in=new FileInputStream(configLocation);
}
else
{
configLocation=clu.getConfigName();
in = clu.getConfig();
//if the input stream is null, then throw a FileNotFoundException like the user-specified
//config strategy will throw and catch below to print out message.
if (in==null)
throw new FileNotFoundException();
}
BufferedReader br=new BufferedReader(new java.io.InputStreamReader(in, "ISO_8859-1:1987"));
boolean done = false;
while(!done)
{
String line = br.readLine();
if (line == null)
done = true;
else
{
v.addElement(line);
++num;
}
}
br.close();
}
catch(FileNotFoundException e1)
{
//I'm having to do the ResourceBundle work manually since the Launcher jar shouldn't depend on any other jars.
ResourceBundle resourceBundle=ResourceBundle.getBundle("com.sas.app.Resources", //$NON-NLS-1$
Locale.getDefault(),
ExtClassLoader.class.getClassLoader());
MessageFormat format=new MessageFormat(resourceBundle.getString("ExtClassLoader.ConfigLocationError.txt")); //$NON-NLS-1$
Object[] messageArgs=new Object[1];
messageArgs[0]=configLocation;
System.err.println(format.format(messageArgs));
}
catch(IOException e2)
{
e2.printStackTrace();
}
// grab the urls from the extensions classloader and see which ones match the jars from the
// list in the config file. We prepend a slash to the jar name so we don't match partials.
String[] filter = (String[])v.toArray(new String[num]);
ClassLoader loader = parent.getParent(); // hope this is the extensions classloader
if (loader instanceof java.net.URLClassLoader)
{
URL[] u = ((java.net.URLClassLoader)loader).getURLs();
v.removeAllElements();
int i;
for(i=0;i<u.length;++i)
{
int j;
for(j=0;j<filter.length;++j)
{
if (u[i].getFile().endsWith("/"+filter[j])) //$NON-NLS-1$
v.addElement(u[i]);
}
}
return (URL[])v.toArray(new URL[0]);
}
return null;
}
}
/**
* ClassLoaderUtil is here only to load the config file as a resource. This allows the config to
* reside inside the jar file.
*/
class ClassLoaderUtil
{
public ClassLoaderUtil()
{
}
public String getConfigName()
{
return "JavaEXTs.config"; //$NON-NLS-1$
}
public InputStream getConfig()
{
return getClass().getResourceAsStream(getConfigName());
}
}
=com/jim/JavaEXTs.config========================================
dnsns.jar
ldapsec.jar
localedata.jar
sunjce_provider.jar
=test.java==================================================
public class test
{
public static void main(String[] args)
{
System.getProperties().list(System.out);
System.out.println("test");
System.exit(0);
}
}
======================================================================
- duplicates
-
JDK-6196407 J2SE NIO: eucJP-open failed to be looked up.
-
- Resolved
-
- relates to
-
JDK-5104480 java.lang.IllegalStateException: recursive invocation using LANG=hu_HU.ISO8859-2
-
- Resolved
-
-
JDK-4838512 (cs) Default charsets must be hardwired
-
- Resolved
-
-
JDK-6240665 java.lang.IllegalStateException: recursive invocation using LANG=ja(japanese)
-
- Closed
-