I have three classes I can't modify. In short, I have a class Program, and other two classes, ProgramClient and ProgramServer, inheriting from Program. The class Program has a static variable.
Until now, I ran ProgramClient and ProgramServer in two different applications, without any problem.
Now I need to run the two classes inside the same application. Doing this, they share the static variable of their parent class, and so bad things happen.
How can I keep the two classes in their own "context" (JVM?) in order to ensure that the static variable is used by only one of the children classes?
Static variables, by definition cannot be overridden or duplicated between classes.
However the two classes can be separated by using two separate class loaders, that are not chained. This is exactly how J2EE containers provider separation between web applications.
In other words, you will load the base class Program into the JVM twice, and sandbox them apart. Thus giving each separate 'program' their own instance. More can be learnt about class loaders here.
Here is a very simple example bootstrap program. Make sure that the code for ProgramClient and ProgramServer are NOT on the system classpath when you start the java command. You will also need to change the url to the jar file which does contain the target program code.
public class Bootstrap {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
URLClassLoader cl1 = new URLClassLoader(
new URL[] {
new URL("file:///client.jar")
},
Bootstrap.class.getClassLoader()
);
URLClassLoader cl2 = new URLClassLoader(
new URL[] {
new URL("file:///server.jar")
},
Bootstrap.class.getClassLoader()
);
invokeAsync(cl1, "a.b.c.ProgramClient");
invokeAsync(cl2, "a.b.c.ProgramServer");
}
private static void invokeAsync(final ClassLoader cl, final String fqn, final String...progArgs) {
new Thread() {
public void run() {
try {
Class p1 = cl.loadClass(fqn);
Class argType = String[].class;
Method m = p1.getMethod("main", argType);
m.invoke(null, (Object) progArgs);
} catch ( Exception e ) {
e.printStackTrace();
}
}
}.start();
}
}
There is no inheritance or override of static method or variable because there is only one reference for it in all the program. That the aim if static.
Maybe you have to create a context class which is instantiate for each program class.
Related
I have some code that I need to reuse in several Java apps. That code implements a GUI which in turn needs to access some static variables and methods from the calling class. Those variables and methods are always called the same in all of the apps. Is there a generic way to obtain a handle to the calling class in Java so the code for "someGUI" class can remain untouched and in fact come from the same source file for all the different apps?
Minimal working example:
import javax.swing.*;
class test {
static int variable = 123;
public static void main(String[] args) {
someGUI sg = new someGUI();
sg.setVisible(true);
}
}
class someGUI extends JFrame {
public someGUI() {
System.out.println(String.format("test.variable = %d", test.variable));
}
}
How can I "generify" the reference to "test" in test.variable to always just refer to the calling class? It's not the "super" class, at least using super.variable doesn't work.
Firstly I would advise against this approach since there are only brittle ways to implement it. You should parameterize SomeGUI with a parameter containing the values you need instead.
However, it is possible to do what you ask by examining the thread's stack trace and using reflection to access the static fields by name. For example like this:
class Test {
static int variable = 123;
public static void main(String[] args) throws Exception {
SomeGUI sg = new SomeGUI();
}
static class SomeGUI extends JFrame {
public SomeGUI() throws Exception {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// stackTrace[0] is getStackTrace(), stackTrace[1] is SomeGUI(),
// stackTrace[2] is the point where our object is constructed.
StackTraceElement callingStackTraceElement = stackTrace[2];
String className = callingStackTraceElement.getClassName();
Class<?> c = Class.forName(className);
Field declaredField = c.getDeclaredField("variable");
Object value = declaredField.get(null);
System.out.println(String.format("test.variable = %d", value));
}
}
}
This will print test.variable = 123.
Obviously this is sensitive to renaming of the variables. It is also sensitive to dynamic proxies.
Also, it should be noted that you need to do this in the constructor. If you try to do this kind of lookup in other methods you can not find out how the instance was created.
There is no inheritance between somGUI and test,
Actual inheritance is there between someGUI and JFrame.
If you use super(), JVM tries to find 'variable' in JFrame, that is not what you wanted.
Use static methods setters & getters to access the 'variable' instead of direct accessing them.
In my program, I already created several global variables, but it doesn't work with loaded files.
I load a .ttf file and create a Font-type variable (Or is it a constant?) for it:
public class Project extends JPanel implements Runnable
{
[...] //global variables
public static void main(String[] args) throws IOException, FontFormatException
{
InputStream input = Project.class.getResourceAsStream("slkscre.ttf");
Font Silkscreen = Font.createFont(Font.TRUETYPE_FONT, input);
#Override
public void run()
{
[...]
}
}
The problem is that if I'd like to make some operations, it can't detect SilkScreen, which means (I think at least) that variables in main are not public.
Anyway, if I do it this way:
public class Project extends JPanel implements Runnable
{
InputStream input = Project.class.getResourceAsStream("slkscre.ttf");
Font Silkscreen = Font.createFont(Font.TRUETYPE_FONT, input);
public static void main(String[] args) throws IOException, FontFormatException
{
#Override
public void run()
{
[...]
}
}
I got the error unreported exception FontFormatException; must be caught of declared to be thrown. I'm new at Java programming so I'd like to ask that what does it mean?
The same happens either if input is declared before main and Silkscreen is declared in run of if both of them are declared in run.
So the main question is how to make an input and a font from it public - or at least, available in run?
The problem is that if I'd like to make some operations, it can't detect SilkScreen, which means (I think at least) that variables in main are not public.
Correct. Local variables are not visible outside of the method or block they are declared in.
The most straightforward solution is to declare the variables in your class and initialize them inside of the main method (or in a constructor).
I have some class A:
public class A {
public A(String str) {
System.out.println("Create A instance: " + str);
}
public void methodA() {
System.out.println("#methodA1()");
}
}
And my class loader implementation:
public class MyClassLoader extends ClassLoader {
public MyClassLoader() {
super();
}
#Override
public synchronized Class<?> loadClass(String name)
throws ClassNotFoundException {
System.out.println("Load: " + name);
return super.loadClass(name);
}
}
And now I try to change default class loader in current thread:
import java.util.ArrayList;
import java.util.List;
public class ChangeLoaderTest {
public static void main(String[] args) {
// Save class loader so that we can restore later.
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
MyClassLoader newLoader = new MyClassLoader();
try {
// Set new classloader.
Thread.currentThread().setContextClassLoader(newLoader);
// My class.
A a = new A("1");
a.methodA();
// Standard Java class.
List<Integer> list = new ArrayList<Integer>();
list.add(2);
list.add(3);
} finally {
// Restore.
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
}
And ChangeLoaderTest output:
Create A instance: 1
#methodA1()
No one
Load: ...
Why? How I can change ClassLoader into some thread?
As Marko Topolnik points out the context classloader is for use by frameworks. To use the classloader yourself you have to call loadClass("somepackage.A") and then use the reflection API to create a new instance of A (Class.newInstance()).
You wont be able to use A or its methods in your source directly since the calling code does not know A - it uses a different classloader. An interface or baseclass of A that can be loaded by the normal classloader can be used to avoid reflection.
interface AIF{
void someMethod();
}
class A implements AIF{
public void someMethod(){}
}
public void test(){
MyLoader loader = new MyLoader();
Class cla = loader.loadClass("A");
AIF a = (AIF) cla.newInstance();
a.someMethod();
}
The contextClassLoader mechanisms is not used by the basic Java operations like new. It's only there so various frameworks can access the context class loader in charge and load resources, classes, etc. Java will always use the classloader that loaded the code that is executing. It's the one that you access via ChangeLoaderTest.class.getClassLoader() -- and there is nothing you can do about this one.
I think that what happens is that your application's class loader which is also your classloader's "parent" can locate A and load it. As a result your classloader will not be searched or used for loading A.
To be honest, I haven't much experience with classloaders but if you subclassed one that uses a URL for the path of the class (so that it can locate the class file) and the parent classloader can not load it (not part of classpath), your custom one will be used.
When a subclass inherits main() from a superclass, is it possible to determine the actual class invoked on the command-line? For example, consider the following two classes, in which main is implemented by A and inherited by B:
public class A {
public static void main(String[] args) throws Exception {
// Replace with <some magic here> to determine the class
// invoked on the command-line
final Class<? extends A> c = A.class;
System.out.println("Invoked class: " + c.getName());
final A instance = c.newInstance();
// Do something with instance here...
}
}
public class B extends A {
}
We can invoke B successfully (i.e., B does 'inherit' main - at least in whatever sense static methods can be inherited), but I have not found a method to determine the actual class invoked by the user:
$ java -cp . A
Invoked class: A
$ java -cp . B
Invoked class: A
The closest I've come is to require that the subclass implement main() and call a helper method in the superclass, which then reads the thread stack to determine the calling class:
public class AByStack {
public static void run(String[] args) throws Exception {
// Read the thread stack to find the calling class
final Class<? extends AByStack> c = (Class<? extends AByStack>)
Class.forName(Thread.currentThread().getStackTrace()[2].getClassName());
System.out.println("Invoked class: " + c.getName());
final AByStack instance = c.newInstance();
// Do something with instance here...
}
public static void main(String[] args) throws Exception {
run(args);
}
}
public class BByStack extends AByStack {
public static void main(String[] args) throws Exception {
// Call the master 'run' method
run(args);
}
}
This method works:
$ java -cp . AByStack
Invoked class: AByStack
$ java -cp . BByStack
Invoked class: BByStack
But I'd really like to eliminate the requirement that subclasses implement main() (yes, call me picky...). I don't mind if it requires some ugly code, since it will be implemented once and buried in the base class, and I'm mostly interested in Sun/Oracle VMs, so I'd be willing to consider using a private sun.misc class or something similar.
But I do want to avoid platform-dependencies. For example, on Linux, we can look at /proc/self/cmdline, but that's of course not portable to Windows (I'm not sure about Mac OS - I don't have my Mac with me at the moment to test this trick). And I think JNI and JVMTI are out for the same reason. I might be wrong about JVMTI, but it looks to me like it would require a C wrapper. If not, perhaps we could use that interface somehow.
This question was asked years ago at http://www.coderanch.com/t/375326/java/java/Getting-command-line-class. The best answer there required a static initializer block in each subclass - a different, but similar requirement on the subclass author to the main calling run() solution I demonstrated. But I haven't seen more recent discussions; I'm hopeful that current VMs might allow access to information that wasn't available at the time of that discussion.
Presumably you want this so that some tool can be called with different names, but behave largely the same depending on the name it was invoked with? Or some similar magic?
In which case, you could simply have an actual main() method in all the sub-classes and delegate to a method which also takes the name of the invoked class:
public class Super {
protected void doMain(String invokee, String... args) {
System.out.println("I was invoked as: " + invokee);
}
}
public class ToolA {
public static void main(String... args) {
new Super().doMain("ToolA", args); // or ToolA.class.getName() to be refactor-proof
}
}
I think I would simply modify the signature of the run() method, and rather than using the call stack, implement main() in each subclass as:
public class BByStack extends AByStack {
public static void main(String[] args) throws Exception {
// Call the master 'run' method
run(BByStack.class, args);
}
}
If all you are doing is instantiating using the default constructor, I would even just pass in
new BByStack()
I don't know of any way to do what you're asking; the method is static, so you can't get a handle to this.getClass() or the like; if the method is not defined on the subclass, you also can't get that information from the stack.
Since I've wasted considerably more time investigating than the problem warranted, I'll post my conclusions here.
First, to try to reiterate the rationale for the question:
I have abstracted argument-handling, I/O, and other shared tasks into an abstract superclass, which I expect others to extend. After performing argument parsing and shared setup, a static method in the superclass instantiates an instance of the subclass and calls its run() method.
Authors of subclasses are encouraged to implement public static void main(String[]) and call the superclass's primary entry point. But, unlike the requirement that all subclasses implement run(), we cannot enforce that requirement statically at compile time (since Java has no concept of an abstract static method).
So I'm trying to implement a main(String[]) method in the superclass which can determine the name of the subclass which was requested on the command-line and instantiate the appropriate class.
I've found two methods, both specific to the Sun / Oracle JVM.
The first uses internal sun.jvmstat classes:
import java.lang.management.ManagementFactory;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.VmIdentifier;
import sun.jvmstat.perfdata.monitor.protocol.local.LocalMonitoredVm;
...
public static String jvmstatMainClass() {
// Determine the VMID (on most platforms, this will be the PID)
final String pid = ManagementFactory.getRuntimeMXBean().getName().split("#")[0];
// Connect to the virtual machine by VMID
final VmIdentifier vmId = new VmIdentifier(pid);
final LocalMonitoredVm lmVm = new LocalMonitoredVm(vmId, 1000);
// Find the requested main-class
String mainClass = MonitoredVmUtil.mainClass(lmVm, true);
// And detach from the VM
lmVm.detach();
return mainClass;
}
The second uses Sun's jps utility:
import java.io.BufferedReader;
import java.io.InputStreamReader;
...
public static String jpsMainClass() {
// Determine the VMID (on most platforms, this will be the PID)
final String pid = ManagementFactory.getRuntimeMXBean().getName().split("#")[0];
// Execute the 'jps' utility
final Process jps = Runtime.getRuntime().exec(new String[] { "jps", "-l" });
final BufferedReader br = new BufferedReader(new InputStreamReader(jps.getInputStream()));
// Parse the output of jps to find the current VM by PID
for (String line = br.readLine(); line != null; line = br.readLine()) {
final String[] split = line.split(" ");
if (pid.equals(split[0])) {
return split[1];
}
}
}
return null;
}
Hopefully my wasted time will prove helpful to someone else.
I'm looking into dynamic modification of classpath. I found one solution that works nicely but it does so using an explicit call to addURL(). (presumably at startup)
However, I would like to intercept the class-loading process at runtime to locate classes if the default classloader can't seem to find them. I tried to subclass ClassLoader so it just delegates findClass() and loadClass() to the default, and print out a debug line telling me these methods have been called, but they never seem to get called when my class uses dependent classes via implicit classloading, e.g.
// regular object instantiation with 'new'
BrowserLauncher launcher;
launcher = new BrowserLauncher();
// static methods
Foobar.doSomethingOrOther();
// Class.forName()
Class cl = Class.forName("foo.bar.baz");
// reflection on a Class object obtained statically
Class<Foobar> cl = Foobar.class;
// do something with cl, like call static methods or newInstance()
How does classloading work under these circumstances? (vs. the simpler case where Classloader.loadClass() is called explicitly)
Here's my attempt at a custom classloader, below. If I use DynClassLoader0.main() with an arguments list of {"some.package.SomeClass", "foo", "bar", "baz"}, and some.package.SomeClass references other classes found in external .jar files, using one of the methods listed above, why doesn't my DynClassLoader0's findClass() and loadClass() get called? The only time loadClass gets called is the explicit call to loadClass in the main() function below.
package com.example.test.classloader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class DynClassLoader0 extends ClassLoader {
public DynClassLoader0()
{
super();
}
public DynClassLoader0(ClassLoader parent)
{
super(parent);
}
public void runMain(String classname, String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
// [***] here we explicitly use our classloader.
Class<?> cl = loadClass(classname);
Method main = cl.getMethod("main", String[].class);
main.invoke(null, new Object[] {args});
}
#Override protected Class<?> findClass(String name) throws ClassNotFoundException
{
System.out.println("findClass("+name+")");
return super.findClass(name);
}
#Override public Class<?> loadClass(String name) throws ClassNotFoundException
{
System.out.println("loadClass("+name+")");
return super.loadClass(name);
}
static public void main(String[] args)
{
// classname, then args
if (args.length >= 1)
{
String[] classArgs = new String[args.length-1];
System.arraycopy(args, 1, classArgs, 0, args.length-1);
ClassLoader currentThreadClassLoader
= Thread.currentThread().getContextClassLoader();
DynClassLoader0 classLoader = new DynClassLoader0(currentThreadClassLoader);
// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(classLoader);
try {
classLoader.runMain(args[0], classArgs);
}
catch (Exception e) {
e.printStackTrace();
}
}
else
{
System.out.println("usage: DynClassLoader {classname} [arg0] [arg1] ...");
}
}
}
edit: I have looked through these questions already:
How do you change the CLASSPATH within Java?
Is it possible to “add” to classpath dynamically in java?
Adding files to java classpath at runtime.
edit: I thought what kdgregory is saying below is correct, that once I use my classloader explicitly (see line in code with [***] as a comment), all the code that executes from that class will cause implicit classloading from the same classloader. Yet my DynClassLoader0.loadClass() never gets called except during the outermost explicit call.
To quote from the ClassLoader JavaDoc:
The methods and constructors of
objects created by a class loader may
reference other classes. To determine
the class(es) referred to, the Java
virtual machine invokes the loadClass
method of the class loader that
originally created the class.
In other words, once you load a class, that class tries to load other classes through the classloader that loaded it. In a normal Java application, that is the system classloader, which represents the classpath passed to the JVM, or the boot classloader, used to load the JVM runtime.
Depending on your needs, there's a variant of Class.forName() that takes a classloader as an argument. If you use this to load a particular class, then references within that class should use the specified classloader.
Edit: I started tracing through your example, but decided it would just be easier to give my own. If you're going to write your own classloader, I suggest starting with the existing URLClassLoader, because it handles a lot of the behind-the-scenes stuff.
So, MyClassLoader takes a single JARfile/directory and loads classes for that directory alone. I've overridden the three methods called to load a class, and simply log their invocation (using System.err because it doesn't buffer output, unlike System.out).
My example uses a library that I'm currently working on; it was convenient, but you can pick any library you want as long as it's not already in your classpath.
The main() method loads a class via MyLoader. Then I invoke a method on that class, in a way that I know will throw an exception that's also part of the library. Note that I invoke the method by reflection: since the library is not on my Eclipse classpath, I couldn't compile it with an explicit reference.
When I run this program (under Sun JDK 1.5 for Linux), I see a lot of calls to loadClass(), both for classes in my library and for those on the classpath. This is expected: the ParseUtil class references a lot of other classes, and will use MyLoader (ie, its classloader) to load them. For those classes that MyLoader can't find locally, it delegates up the loader tree.
The exception is thrown, and when I print out its classloader I see that it's the same as the MyLoader instance I created. I also print out the loader for Exception.class, and it's null -- which the JavaDoc for Class.getClassLoader() says indicates the boot classloader.
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class ClassLoaderExample
{
private static class MyClassLoader
extends URLClassLoader
{
public MyClassLoader(String path)
throws Exception
{
super(new URL[] { new File(path).toURL() });
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
System.err.println("findClass(" + name + ")");
return super.findClass(name);
}
#Override
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
System.err.println("loadClass(" + name + "," + resolve + ")");
return super.loadClass(name, resolve);
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
System.err.println("loadClass(" + name + ")");
return super.loadClass(name);
}
}
public static void main(String[] argv)
throws Exception
{
ClassLoader myLoader = new MyClassLoader("/home/kgregory/Workspace/PracticalXml-1.1/target/classes/");
System.out.println("myLoader = " + myLoader);
Class<?> parseUtilKlass = myLoader.loadClass("net.sf.practicalxml.ParseUtil");
Method parseMethod = parseUtilKlass.getDeclaredMethod("parse", String.class);
try
{
parseMethod.invoke(null, "not at all valid XML");
}
catch (InvocationTargetException e)
{
Throwable ee = e.getCause();
System.out.println("exception:" + ee);
System.out.println("exception loader = " + ee.getClass().getClassLoader());
System.out.println("Exception.class loader = " + Exception.class.getClassLoader());
}
}
}
Edit #2, based on today's comments.
A classloader is expected to delegate requests to its parent before it attempts to fulfill the request itself (this is in the ClassLoader JavaDoc). There are a couple of benefits to this practice, foremost being that you won't unintentionally load incompatible instances of the same class.
J2EE classloaders amend this model: the classloader used to load a WAR will attempt to resolve classes before the loader for a containing EAR, which in turn attempts to resolve classes before the container's classloader. The goal here is isolation: if both the WAR and its EAR contain the same library, it's probably because the two need differing versions (that, or they have a sloppy build process). Even in the J2EE case, I believe that the container classloader delegates in the standard way.
In your code the call to super.loadClass() delegates the loading of the class to the parent classloader (just look at the implementation of java.lang.ClassLoader#loadClass). So it is not your instance of DynClassLoader0 that loads the class, but the currentThreadClassLoader (which you took from Thread.currentThread().getContextClassLoader()) that you passed as a constructor parameter to DynClassLoader0. And when the loaded class refers to other classes, they are then also loaded by that classloader and not your DynClassLoader0.