Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (526)
Games in Android Showcase (127)
games submitted by our members
Games in WIP (593)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Getting info on a class without instantiating it..  (Read 4740 times)
0 Members and 1 Guest are viewing this topic.
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Posted 2003-11-14 10:41:13 »

I want to be able to get various non-trivial info from classes which have come from some external source. Typical scenario is that someone supplies you with a load of new classes, and you want to check versioning information etc before you even allow the rest of your system to see them.

The easy way is to use OOP and instantiate each and every class into an instance of some interface that has appropriate query methods (NB: I can (and in fact AFAICS have to, due to the vagaries of Classloaders!) mandate compliance with whatever interface I desire).

But I want to get this info without instantiating them...

I can also insist that they extend a base class that already exists (and is partially abstract).

How can I interact with the classes just after they've been loaded, but before they're instantiated? (NB: one of the things I'm aiming for is to be able to do certain checks and logging within the Classloader. Because loading a single class can pull in multiple others all in a single method call, I may need the info from Class A before it is physically possible to instantiate A (because I need to know before I allow the loading of Class B, referenced by A)).

It would be beautiful if there were a static inheritance tree where *methods* were inherited but not *variables*. That way, you could write a method body, e.g. "getVersion()", that used the *current* class's static var "version", but the method implementation itself was inherited to subclasses. They would just need to mask the superclass's "version" var with their own, and when getVersion() was invoked on them, it would use their value.

AFAIAA, this is impossible in Java? It wouldn't surprise me, given it's a bit of a weird feature Smiley.

AFAICS, the best way of achieving my aims within the bounds of the JLS Smiley is to just use reflection and have a "known list" of methods that imported classes have to implement as static's.

It'll work fine, and the behaviour when loading a class that doesn't have the desired method(s) will be a proprietary "ClassIncompatible"  Exception that indicates to the rest of our sytem to message the admin / logfiles / etc that Class XXX is missing a required method. That's a cop-out, but OK because it bails out *before* any system or game state is changed, and an admin trying to introduce that class will get notified pretty much immediately that they've screwed up Smiley.

malloc will be first against the wall when the revolution comes...
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #1 - Posted 2003-11-14 10:46:23 »

I'm not sure whether the funked up inheriting I describe above violates OOP or is a natural part of it. Inheriting method implementations and performing lazy unification of the impl with the variables violates the "object = data + code" principles, but the kind of programming it allows strikes me as being very much within the general principles of inheritance within OOP.

That said, AFAICS it falls into the category of "language features that  make many optimizations really difficult or imposible" - like pointers.

malloc will be first against the wall when the revolution comes...
Offline cfmdobbie

Senior Devvie


Medals: 1


Who, me?


« Reply #2 - Posted 2003-11-14 13:02:50 »

*brain melts*

You're trying to do something really, really evil here.  I don't think Java is going to help much, if at all.  The known-static approach seems like the best one to me, unless there are static initialisers that you don't want to allow to run, either?  I guess if you're paranoid enough about instantiating a class, you're dealing with totally untrustworthy code.  In this situation there are a large number of libraries out there that can manipulate bytecode directly - maybe one of those could be useful to you?

I haven't researched these, but one of the following might work:


Edit: Sorry,  forgot to say:  "Nutter!" Grin

Hellomynameis Charlie Dobbie.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #3 - Posted 2003-11-14 16:11:38 »

Quote
*brain melts*

You're trying to do something really, really evil here.


Chuckle. I accept it's "evil", but...(c.f. below) the outcome is to make life much easier for people who develop with the system / APIs.

IME you normally have to tie yourself in knots in java and produce horrible code if you want to provide a really easy-to-use yet *not sacrficing expressive power* API to other developers....

Quote

unless there are static initialisers that you don't want to allow to run, either?


Just to be clear, I'm not hoping to deal with deliberately dangerous code, which would be a different kettle of fish.

More generally, I'm trying to place automatic checking in the system (of which this is one) where programmer mistakes are caught and dealt with at the earliest possible opportunity, with similar aims and advantages as compile-time type checking.

One problem with allowing full class loading before checking whether to use a class is that you have no protection from someone instantiating a class and using it without asking the system to check the versioning. Sure, you can have a "process" where programmers are supposed to only use objects from a system-created pool, but it's much much safer (and much neater for people developing with the system) if it's all automatic, and they dont' have to worry about it.

E.g. we had a set of classes and methods where you could instantiate interfaces whenever you wanted an Object, by calling a <code>instantiateInterface( String interfaceName )</code> method. This is the same as the Factory pattern. We've been migrating them into a Classloader, so that you can replace code that looks like:

1  
2  
   BlahFactory f = Factories.getFactoryFor( "blah" );
   Blah b = f.getInstance( config.getPort(), config.getIPAddress() );


with code that looks like:

1  
   iBlah b = new Blah();


(where "iBlah" is an interface, and there is no Blah class, but our custom classloader understands what to do, what actual implementation of iBlah to use, and where to look for the arguments for the constructor if it doesn't have a 0-arg constructor)

The main reason for doing things like this is that we're making middleware for other developers to use, and we want as much as possible for them to be able to write "ordinary" java code, and not have to change their working practices, and yet get various benefits of our system transparently.

By putting versioning directly into the Classloader at class definition time, there's no need for the end-user (developer) to be aware he has to call "checkVersion( Class )". More importantly, there's no boiler plate code for them to write (e.g. no need to call "checkVersion()" on every class they intend to use at the start of their classfile. Yuk! I've spent too much time over the years on API's where you had to include that kind of boilerplate code Sad ).

...and if it looks like I'm going about this the wrong way, give me a shout Smiley. I asked the originakl question mainly to check there wasn't some JLS feature I hadn't noticed which would be simpler / better than doing this via reflection....

malloc will be first against the wall when the revolution comes...
Offline Jeff

JGO Coder




Got any cats?


« Reply #4 - Posted 2003-11-14 17:31:56 »

The answer is easy to say and hard to code:

Write a custom classloader and override findClass().

This will give you control of the bits read in and when they get instantiated into a calss.

Now the HARD part...

Break out your VM manual because what you have is the raw binary representation of a class as it exists in a .class file.  You are going to have to pull it apart and dig out what youa re inetrested in yourself.  if its code you care about your goping to have to interpret bytecode.

In a nutshell what you are writing is your own bytecode verifier... an ugly and nasty bit of programming any way you look at it.

JK

Got a question about Java and game programming?  Just new to the Java Game Development Community?  Try my FAQ.  Its likely you'll learn something!

http://wiki.java.net/bin/view/Games/JeffFAQ
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #5 - Posted 2003-11-14 17:54:46 »

Quote
The answer is easy to say and hard to code:

Write a custom classloader and override findClass().

This will give you control of the bits read in and when they get instantiated into a calss.


Oh, that's the easy bit Smiley. I've done custom Classloader's before - I'm not having any difficulty with the Classloader part (well, not since the "new-style" Classloaders from 1.2.x onwards... Smiley)

Quote

Now the HARD part...

Break out your VM manual because what you have is the raw binary representation of a class as it exists in a .class file.  You are going to have to pull it apart and dig out what youa re inetrested in yourself.  if its code you care about your goping to have to interpret bytecode.

In a nutshell what you are writing is your own bytecode verifier... an ugly and nasty bit of programming any way you look at it.


nononono - I don't want to check the bytecodes - as I said, I'm *not* doing this for security reasons; I'm doing it so that when someone tries to construct an Object which uses any of the classes from this Classloader, I can GUARANTEE that my version checking (the example I gave - but there are a few other things I probably want to do at the same time) code has been executed against the imported class AND that a failure in the version check / etc will GUARANTEE to prevent the class from being instantiated.

The main thing is to make it that all classes are having some clever checking done against them (not security) WITHOUT the end-developer having to be aware of this and alter his/her coding style...

There are other uses too, such as helping our class-reloading system (which ultimately needs to reload a class - live - without corrupting the system).

malloc will be first against the wall when the revolution comes...
Offline swpalmer

JGO Coder


Exp: 12 years


Where's the Kaboom?


« Reply #6 - Posted 2003-11-14 22:20:25 »

I think the link above to the Byte Code Engineering Library would be helpful for what you want.   But it will still be a lot of work.

Offline Jeff

JGO Coder




Got any cats?


« Reply #7 - Posted 2003-11-15 02:16:08 »

Quote


nononono - I don't want to check the bytecodes -
<... snip ...>
someone tries to construct an Object which uses any of the classes from this Classloader, I can GUARANTEE that my version checking (the example I gave - but there are a few other things I probably want to do at the same time) code has been executed against the imported class AND that a failure in the version check / etc will GUARANTEE to prevent the class from being instantiated.


I don't understand what you mean by "version check".  

If you mean checking the value of a static like the VersionUID, then its still a byte code issue.  You will need to crack your VM manual and find out how those statics are stored in the byte code and dig them out.  When we say "bytecode" we don't just mean opcodes.  The bytecode is the COMPLETE binary representation of the class.  All that is documented  in the VM spec.

In the end you are either interpreting bytecode or instantiating. There IS no other way by definition to get information on a class.

Got a question about Java and game programming?  Just new to the Java Game Development Community?  Try my FAQ.  Its likely you'll learn something!

http://wiki.java.net/bin/view/Games/JeffFAQ
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #8 - Posted 2003-11-15 09:26:07 »

Quote


I don't understand what you mean by "version check".  



Nothing to do with classfiles, but high-level versioning within the application layer. Java has no proper versioning system at the moment (there are the numerous hacks in manifests, but they don't go anywhere near far enough to be useful as a proper versioning system. For instance, there isn't enough info to resolve version-based dependencies and similar relationships).

And when you're talking about an environment with classes being reloaded, versioning becomes even more important than before.

Quote

In the end you are either interpreting bytecode or instantiating. There IS no other way by definition to get information on a class.


So, what's a static method?

Alternatively, what does instantiation mean? (I'd always been taught it was the act of making an instance - creating an Object out of a class)

malloc will be first against the wall when the revolution comes...
Offline princec

« JGO Spiffy Duke »


Medals: 423
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #9 - Posted 2003-11-15 11:41:12 »

The subtle distinction you're missing is that here you are instantiating a Class, not an instance of that Class. (As classes are themselves objects. Class is derived from Object, remember?)

From then on you could use a static initializer in the class to perform the version check against a custom classloader:
1  
2  
3  
4  
class Blah {
static {
MyClassLoader mcl = (MyClassLoader) Blah.class.getClassLoader();
}

If the class is loaded with the wrong classloader it'll throw a ClassCastException and the class will fail to load (ultimately throwing a ClassNotFoundException in the calling classloader).

Otherwise you can then use reflection on the class in MyClassLoader to pull out a version field and compare it against whatever.

Cas Smiley

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #10 - Posted 2003-11-15 12:55:51 »

Quote
The subtle distinction you're missing is that here you are instantiating a Class, not an instance of that Class. (As classes are themselves objects. Class is derived from Object, remember?)


Sorry, when Jeff said you *had* to use bytecode or insantiation, I thought he meant instantiation of the class, not of the Class. I'm not sure there's any reason to avoid instantiating a Class? But my previous experiences with Classloader's haven't delved into how reflection interacts with the class-loading process....

malloc will be first against the wall when the revolution comes...
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #11 - Posted 2003-11-15 13:35:43 »

Quote

From then on you could use a static initializer in the class to perform the version check against a custom classloader:
1  
2  
3  
4  
class Blah {
static {
MyClassLoader mcl = (MyClassLoader) Blah.class.getClassLoader();
}

If the class is loaded with the wrong classloader it'll throw a ClassCastException and the class will fail to load (ultimately throwing a ClassNotFoundException in the calling classloader).

Otherwise you can then use reflection on the class in MyClassLoader to pull out a version field and compare it against whatever.


That's quite neat, although not - I think? - part of what I'm looking for. The classes that I'm loading should provide data about their own version, and any requirements they have for other classes to be of certain versions. This is a simple way of putting it, but there can be complex relationships that need to be expressed there. (NB we've got a working system that does this, it's just that it acts on Objects not classes, and hence cannot be integrated into a Classloader as-is).

The perfect outcome is that the loaded classes have very little code in them relating to any of this. If this were all being done with pre-instantiated objects, then OOP inheritance can reduce the amount of code that the classes have for this to almost nothing, so long as they extend a base class that handles much of it for them (the per-class code can be as simple as just initializing a few protected data-structures with info about what version this class is, what other versions of other classes it requires, etc).

Unfortunately, that kind of inheritance doesn't work with static methods (hence my "it would be beautiful if..." above). The problem is that an inherited static variable is shared between all classes that subclass the original declarer...it works with non-static methods because although each subclass shares the inherited variable with it's ancestors, those ancestors have a separate copy of the variable for each subclass of themselves in the VM.

So, with non-static inheritance you can share data with "all your ancestors, but no-one else". With static inheritance, you can ONLY share data with "all ancestors AND ALL
their descendents". At least, AFAICS. Which is part of why I was asking - perhaps there is some way of sharing data only with ancestors but not their other descendents, *from within a static context*?

My suggestion that I could use reflection for this is actually, I now realise, pointless. I'd been assuming it was necessary in order to guarantee getting the most specialized implementation of a static method. However, IIRC if I cast a loaded class to a base-class and invoke a static method on that class, java does use the overriden version, not the base-class version (I recall this because I used to play around with situations where you want to invoke the superclass's implementation but you cannot without getting a reference to
1  
super
for that class).

The problem still exists that every single class that anyone ever writes is going to have copy-n-paste the same darn code over and over again. This seems to be one of those cases where JLS's over-simplification of typing and inheritance prevents you from using OOP properly (i.e. in a situation where you want to, and would benefit from doing so).

AFAICS now, the only way of avoiding this is to mandate the presence of certain variables, and place the methods that use those variables inside the Classloader (or a helper class). Arguably, this is a better location for the methods - but when a class has some exceptional behaviour, it won't be able to override them, except by providing a new helper class and asking the Classloader to use the supplied helper class to process the to-be-loaded class, instead of using the "default" helper class.

malloc will be first against the wall when the revolution comes...
Offline cfmdobbie

Senior Devvie


Medals: 1


Who, me?


« Reply #12 - Posted 2003-11-15 16:14:28 »

Can't you require versioning and dependency information to be put into private static fields, so nothing gets shared downwards?

Then you can read those fields from the bytecode in your classloader (bomb out if they're not present), and append a static initialiser to integrate the new class with your system if required?

Hellomynameis Charlie Dobbie.
Offline Jeff

JGO Coder




Got any cats?


« Reply #13 - Posted 2003-11-16 00:28:51 »

From your original question I had assumed you wanted to avoid instantiation of the Class for some reason.

If that not an issue then its much easier.  You can require a static field that contains the version and just check it in your classloader's findClass() call.

Got a question about Java and game programming?  Just new to the Java Game Development Community?  Try my FAQ.  Its likely you'll learn something!

http://wiki.java.net/bin/view/Games/JeffFAQ
Offline Jeff

JGO Coder




Got any cats?


« Reply #14 - Posted 2003-11-16 00:31:50 »

By the way... a reason for avoiding Class instantiation is to prevent Class initializer code from being run for a class you end up rejecting.

Got a question about Java and game programming?  Just new to the Java Game Development Community?  Try my FAQ.  Its likely you'll learn something!

http://wiki.java.net/bin/view/Games/JeffFAQ
Offline cfmdobbie

Senior Devvie


Medals: 1


Who, me?


« Reply #15 - Posted 2003-11-16 07:08:00 »

But we are talking about not instantiating it.  I'm talking about getting the data out of the bytecode, so you can check it before anything reaches the VM.

Hence the bytecode manipulation libraries I mentioned at the beginning.

Hellomynameis Charlie Dobbie.
Offline princec

« JGO Spiffy Duke »


Medals: 423
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #16 - Posted 2003-11-16 07:42:59 »

I have the strongest suspicion here that Adam can solve this problem with protected singleton instances and normal inheritance, using that custom classloader idea.

Why don't you attempt to make a 3-class heirarchy proof of concept?

Cas Smiley

Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #17 - Posted 2003-11-16 07:46:53 »

Quote
By the way... a reason for avoiding Class instantiation is to prevent Class initializer code from being run for a class you end up rejecting.


What is "Class initializer code"?

e.g. I was under the impression that static initializer code for a class was automatically run when a class was loaded, irrespective of whether you went and looked at a Class.

But I'm going to go and re-read the JLS at this point, otherwise I'm in danger of confusing myself (and everyone else Smiley).

I've already discovered (contrary to my recent comment, but agreeing with my original stance) that if you cast an Object to a certain class, and then call a static method, you call the static method for the type you've cast to (as opposed to any subclass). Although the opposite is, of course, true for instance methods. Oh, the pain! It's like learning a particularly irritating foreign language, where there are many more exceptions than rules Sad.

malloc will be first against the wall when the revolution comes...
Offline cfmdobbie

Senior Devvie


Medals: 1


Who, me?


« Reply #18 - Posted 2003-11-16 07:55:36 »

Quote
I've already discovered (contrary to my recent comment, but agreeing with my original stance) that if you cast an Object to a certain class, and then call a static method, you call the static method for the type you've cast to (as opposed to any subclass). Although the opposite is, of course, true for instance methods.

Grin Yep, the trick is that anything static is resolved at compile-time, not run-time.

That one rule, if properly made clear, would probably save thousands of hours of developer time worldwide...

Hellomynameis Charlie Dobbie.
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #19 - Posted 2003-11-16 07:57:53 »

Quote

e.g. I was under the impression that static initializer code for a class was automatically run when a class was loaded, irrespective of whether you went and looked at a Class.


From JLS 2:

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

   * T is a class and an instance of T is created.
   * T is a class and a static method declared by T is invoked.
   * A static field declared by T is assigned.
   * A static field declared by T is used and the reference to the field is not a compile-time constant (ยง15.28). References to compile-time constants must be resolved at compile time to a copy of the compile-time constant value, so uses of such a field never cause initialization

malloc will be first against the wall when the revolution comes...
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #20 - Posted 2003-11-16 08:12:43 »

Quote

Grin Yep, the trick is that anything static is resolved at compile-time, not run-time.

That one rule, if properly made clear, would probably save thousands of hours of developer time worldwide...


Although, counter-intuitively (to me, at least), function despatch is also resolved at compile-time.

I.e. the selection of which method to invoke based on signature etc is performed at compile-time. This is why when you provide two methods with signatures that differ only in that some of the args are subtypes of some of the others, the JVM does NOT choose which one to invoke based upon the types of the arguments in the invocation; it was determined at compile-time. This is a major bummer when trying to make libraries which are automatically polymorphic (i.e. anyone can choose to specialise any of the classes which generate objects your lib passes to arguments, and your library will seamlessly adopt the newly specialised class).

IMHO, Java is generally a poor tool for writing API's Sad.

malloc will be first against the wall when the revolution comes...
Offline blahblahblahh

JGO Coder


Medals: 1


http://t-machine.org


« Reply #21 - Posted 2003-11-16 08:17:46 »

Quote
I have the strongest suspicion here that Adam can solve this problem with protected singleton instances and normal inheritance, using that custom classloader idea.


In the spirit of the forum Wink, how? Sorry to be thick here, but I'm getting stuck trying to work out how it will work without requiring copy-n-paste code into each loaded class.

PS I chose Newless Cluebies because I really find Classloader stuff hard-going (despite having used them quite a bit), and count myself pretty rubbish at making complex Classloader's Sad. I usually stumble over traps in the JLS (basically subtle things I'd not noticed before, or had forgotten since the last time I was working with loaders Smiley).

malloc will be first against the wall when the revolution comes...
Offline cfmdobbie

Senior Devvie


Medals: 1


Who, me?


« Reply #22 - Posted 2003-11-16 09:36:14 »

Quote
I.e. the selection of which method to invoke based on signature etc is performed at compile-time. This is why when you provide two methods with signatures that differ only in that some of the args are subtypes of some of the others, the JVM does NOT choose which one to invoke based upon the types of the arguments in the invocation; it was determined at compile-time.

There's no alternative in this situation.  As an overloaded (not overridden) method has a different method signature, it can throw different exceptions and return different values.  And allowing the runtime to invoke based on the arguments' runtime types in some circumstances but not others would cause too much chaos to be worthwhile.

This is definitely an area where some extension to the language would make a lot of sense - being able to "extend" a method so as to provide different arguments but leave the rest of the signature alone would be an interesting possibility.  The only workaround I've seen that makes much sense is to turn each method into a sort of "callback", where the object of the specific type is asked to call a specific method on the calling object, using a correctly-typed reference.  It ain't cute, but it works. :-/

Quote
In the spirit of the forum Wink, how? Sorry to be thick here, but I'm getting stuck trying to work out how it will work without requiring copy-n-paste code into each loaded class.

No need for any copy-n-paste.  As I said, you can use one of the bytecode manipulators to append your boiler-plate code to the class after you've read it from disk but before letting the VM know about it.

Hellomynameis Charlie Dobbie.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

toopeicgaming1999 (71 views)
2014-11-26 15:22:04

toopeicgaming1999 (60 views)
2014-11-26 15:20:36

toopeicgaming1999 (14 views)
2014-11-26 15:20:08

SHC (27 views)
2014-11-25 12:00:59

SHC (25 views)
2014-11-25 11:53:45

Norakomi (31 views)
2014-11-25 11:26:43

Gibbo3771 (25 views)
2014-11-24 19:59:16

trollwarrior1 (38 views)
2014-11-22 12:13:56

xFryIx (77 views)
2014-11-13 12:34:49

digdugdiggy (55 views)
2014-11-12 21:11:50
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!