FIX: Class Must Be Touched for Static Initialization to Occur When Loaded by SecurityClassLoader
ID: Q219280
|
The information in this article applies to:
-
Microsoft virtual machine
-
Microsoft Visual J++, versions 1.0, 1.1, 6.0
-
Microsoft SDK for Java, versions 1.0, 1.5, 1.51, 2.0, 2.01, 2.02, 3.0, 3.0 Preview 1, 3.1
SYMPTOMS
When deriving from the SecurityClassLoader class, as described in the SDK for Java documentation, to associate permissions with loaded classes, the loaded classes' static variables are not initialized until the class is touched by either instantiating a new instance or calling a method on the class. This behavior is by default.
CAUSE
An optimization flag is set to on by default.
RESOLUTION
This problem has been fixed in build 3155 of the Microsoft virtual machine. If you are using the Microsoft Raw Native Interface (RNI), you can work around this problem by setting a private member variable of the SecurityClassLoader class. Please see the RNI sample below.
STATUS
Microsoft has confirmed this to be a problem in the Microsoft products listed
at the beginning of this article.
MORE INFORMATION
Steps to Reproduce Behavior
- Compile and run the following code.
- Follow the instructions in the code comments.
- If you are running on a Microsoft virtual machine build older than 3155, the values of the static variables have not been set.
- Otherwise, the values appear in the MessageBox as expected.
Java Code
import java.io.*;
import com.ms.security.*;
import com.ms.win32.*;
public class SecureLoader extends SecurityClassLoader
{
private static File f;
private FileInputStream fis;
private byte[] loadClassData(String class_name) throws Exception
{
// Change this as needed to reflect the path
// to the appropriate .class file TestClass.class.
// Note that you will also want to make sure that
// the class file does not exist on the system
// CLASSPATH, or it will be loaded by the System
// ClassLoader and not your custom SecurityClassLoader.
f = new File("D:/VJProj/RNIBug/" + class_name + ".class");
synchronized (f)
{
try
{
byte[] data = new byte[(int)f.length()];
fis = new FileInputStream(f);
fis.read(data);
return data;
}
finally
{
fis.close();
}
}
}
protected Class loadClass(String class_name, boolean resolve_class)
throws ClassNotFoundException
{
Class cl = null;
try
{
cl = findSystemClass(class_name);
}
catch (Exception e)
{
try
{
byte[] class_data = loadClassData(class_name);
PermissionDataSet pds = new PermissionDataSet();
pds.setFullyTrusted(true);
PermissionSet perms = new PermissionSet(pds);
cl = defineClass(class_name,
class_data,
0,
class_data.length,
perms,
(java.security.Principal)null);
}
catch (Exception e2)
{
WinUtil.alert(e2);
}
}
if (resolve_class)
resolveClass(cl);
return cl;
}
}
class WinUtil
{
public static void alert(Object o)
{
User32.MessageBox(0,
o.toString(),
"Alert",
0);
}
}
/***************************************************/
import java.lang.reflect.*;
public class TestRun
{
public static void main(String[] args)
{
try
{
SecureLoader sl = new SecureLoader();
Class cl = sl.loadClass("TestClass", true);
// Comment the following two lines to reproduce the problem.
Object o = cl.newInstance();
WinUtil.alert(o);
// Indicates that we have an instance of TestClass.
// We are unable to typecast this Object to TestClass, as
// TestClass was loaded by the custom ClassLoader SecureLoader.
Field f = cl.getField("staticBlockInt");
int staticBlockInt = f.getInt(cl);
f = cl.getField("staticInt");
int staticInt = f.getInt(cl);
String message = "This string is a result ";
message += "of the TestClass.testMethod() call.\n";
message += "The value of staticInt is "+staticInt+"\n";
message += "The value of staticBlockInt is "+staticBlockInt+"\n";
WinUtil.alert(message);
}
catch (Exception e)
{
WinUtil.alert(e);
}
}
}
/***********************************************************/
public class TestClass
{
public static int staticInt = 10;
public static int staticBlockInt;
static
{
staticBlockInt = 20;
}
// We are over-riding toString to indicate
// that we do indeed have an instance of the
// class that we want.
public String toString()
{
return "TestClass";
}
}
RNI CODE
NOTE: Make sure the Java classes used in the following code are on the CLASSPATH.
#include <windows.h>
#include <stdio.h>
#include <native.h>
void checkForError();
void main(int argc, char *argv[])
{
static const int LATE_CLINIT = -1;
static const int EARLY_CLINIT = 1;
static const int NOT_INITIALIZED = 0;
static int staticInt = 0;
static int staticBlockInt = 0;
int initval = 0;
ThreadEntryFrame threadEntryFrame;
ClassClass* pcMyLoader = NULL;
ClassClass* pcLoadedClass = NULL;
HObject* phMyLoaderInst = NULL;
fieldblock* pFieldBlock = NULL;
if (PrepareThreadForJava(&threadEntryFrame))
{
pcMyLoader = FindClass(NULL, "SecureLoader", TRUE);
checkForError();
if (pcMyLoader)
{
phMyLoaderInst = execute_java_constructor(
NULL,
"SecureLoader",
pcMyLoader,
"()");
} else {
printf("SecureLoader not found.");
Sleep(3000);
}
if (phMyLoaderInst)
{
pFieldBlock = Class_GetField(pcMyLoader,
"initialized");
initval = Field_GetValue(phMyLoaderInst,
pFieldBlock);
printf("The value of initialized is %d.\n",
initval);
// This switch block is the work-around.
// Comment this out to reproduce the problem.
switch (initval)
{
case LATE_CLINIT:
Field_SetValue(phMyLoaderInst,
pFieldBlock,
EARLY_CLINIT);
pFieldBlock = Class_GetField(pcMyLoader,
"initialized");
initval = Field_GetValue(phMyLoaderInst,
pFieldBlock);
break;
case NOT_INITIALIZED:
checkForError();
break;
case EARLY_CLINIT:
break;
}
printf("The value of initialized is now: %d\n",
initval);
pcLoadedClass = (ClassClass*)execute_java_dynamic_method(
NULL,
phMyLoaderInst,
"loadClass",
"(Ljava/lang/String;Z)Ljava/lang/Class;",
makeJavaString("TestClass", strlen("TestClass")),
TRUE);
} else
checkForError();
if (pcLoadedClass)
{
pFieldBlock = Class_GetField(pcLoadedClass,
"staticInt");
staticInt = Field_GetValue(NULL,
pFieldBlock);
printf("The value of staticInt is %d.\n",
staticInt);
pFieldBlock = Class_GetField(pcLoadedClass,
"staticBlockInt");
staticBlockInt = Field_GetValue(NULL,
pFieldBlock);
printf("The value of staticBlockInt is %d.\n",
staticBlockInt);
Sleep(3000);
} else
checkForError();
UnprepareThreadForJava(&threadEntryFrame);
}
}
void checkForError()
{
if (getPendingException(NULL))
{
exceptionDescribe(NULL);
Sleep(3000);
exceptionClear(NULL);
}
}
REFERENCES
For the latest Knowledge Base articles and other support information on Visual J++ and the SDK for Java,
please see the following pages on the Microsoft Technical Support site:
http://support.microsoft.com/support/visualj/
http://support.microsoft.com/support/java/
© Microsoft Corporation 1999, All Rights Reserved.
Contributions by Robert LaCasse, Microsoft Corporation
Additional query words:
Keywords : kbJavaVM kbJNative kbSDKJava kbSecurity kbVJ600fix kbGrpJava
Version : WINDOWS:1.0,1.1,1.5,1.51,2.0,2.01,2.02,3.0,3.0 Preview 1,3.1,6.0
Platform : WINDOWS
Issue type : kbbug