aspose file tools*
The moose likes Java in General and the fly likes How make true Singleton, even if loaded by Class.forName? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of JavaScript Promises Essentials this week in the JavaScript forum!
JavaRanch » Java Forums » Java » Java in General
Bookmark "How make true Singleton, even if loaded by Class.forName?" Watch "How make true Singleton, even if loaded by Class.forName?" New topic
Author

How make true Singleton, even if loaded by Class.forName?

Robert Paris
Ranch Hand

Joined: Jul 28, 2002
Posts: 585
I have a class that will be loaded and instantiated by other people's code via reflection and via explicit loading (e.g. new MyClass() ). I want to always have the same instance of the class throughout the entire JVM, how would I do this?
Dmitry Melnik
Ranch Hand

Joined: Dec 18, 2003
Posts: 328
How would you like this:
Robert Paris
Ranch Hand

Joined: Jul 28, 2002
Posts: 585
Yeah, except when you create it in reflection, it won't use that constructor. So how do I return the singleton instance when they use reflection to instantiate it? Is there a way?
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
It won't? How exactly do you create an instance of the singleton class using reflection? Class.forName() just creates the Class object, not an instance of the class.
If the singleton is Serializable, the constructor is not used to create an instance, and you need a readResolve() method to ensure it's really a singleton. But I'm not aware of a way to bypass the constructor when creating an instance with synchronization.
I was about to post the same answer as Dmity, but he beat me to it. Well, I'd have synchronized for good measure, but otherwise, same idea.
[ February 06, 2004: Message edited by: Jim Yingst ]

"I'm not back." - Bill Harding, Twister
Robert Paris
Ranch Hand

Joined: Jul 28, 2002
Posts: 585
Wait, maybe I'm misunderstanding something, but I thought creation by reflection (i.e. Class.forName(...).newInstance() ) did not use the constructor. Am I wrong on that? Is it just deserialization that doesn't use the constructor?
One thing: they need to be able to useClass.forName(...).newInstance() if my only constructor is private, they can't. They do have to be able to use newInstance(). So how would I make a singleton that can handle such a situation?
Also, what happens when they use another ClassLoader (you can pass one to Class.forName)? What if that Classloader is non-delegating?
[ February 06, 2004: Message edited by: Robert Paris ]
[ February 06, 2004: Message edited by: Robert Paris ]
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
Wait, maybe I'm misunderstanding something, but I thought creation by reflection (i.e. Class.forName(...).newInstance() ) did not use the constructor. Am I wrong on that?
You're wrong on that. newInstance() is essentially a convenience method equivalent to
getConstructor(new Class[] {}).newInstance(new Object[] {})
Is it just deserialization that doesn't use the constructor?
That's the only one I can think of. There may be something weird lurking in RMI or somewhere else, but I don't think so. RMI does use serialization (among other techniques), but that's already covered.
One thing: they need to be able to useClass.forName(...).newInstance() if my only constructor is private, they can't. They do have to be able to use newInstance(). So how would I make a singleton that can handle such a situation?
I don't think you can. If you must make newInstance() usable, it's not going to be a singleton. Those are fundamentally incompatible goals as far as I can see. Well, there's one tiny exception. You can use something like:

It's now publicly accessible, but can be used only once or you get an exception. Which does guarantee it's a singleton, and allow you to use newInstance() - but the one-time-only limit probably wasn't what you wanted. In which case one of those requirements is going to have to go, I fear.
Robert Paris
Ranch Hand

Joined: Jul 28, 2002
Posts: 585
I feared that was the case. What about this: can I sort of "fake" a Singleton, if I make everything use static variables and methods? There are non-static methods on the object/class that I have to keep, but I could make those always use private static ones (synchronized of course). So even though they'd always be getting a new object, every one of the objects would be accessing exactly the same data and methods (which is essentially what I want).
For example:
if there were a method named: public void getName(), I could have it be something like:

And setting the name would be synchronized on the class and set the static variable.
Would this "essentially" give me a singleton? Am I correct that this would work, or would a non-delegating Classloader ruin this?
Chris Mathews
Ranch Hand

Joined: Jul 18, 2001
Posts: 2712
What you are referring to isn't a Singleton. It is a pattern known as Monostate. See this for more information on Monostate.
The big question is why do you need this? There are very few cases where I think the use of Singletons or Monostates is a good design.
Robert Paris
Ranch Hand

Joined: Jul 28, 2002
Posts: 585
I'm creating my own Policy (extends java.security.Policy) and the JRE source code has two methods where people get the Policy object:
getPolicy()
getPolicyNoCheck()
In both methods (the second of which is package private and I cannot override) returns a new instance everytime (by using reflection and newInstance() ). This of course makes caching information in the Policy object pretty tough/useless and makes a dynamic policy pretty tough as well. I'm not sure why they chose a non-singleton architecture here, but they did.
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
In both methods (the second of which is package private and I cannot override) returns a new instance everytime (by using reflection and newInstance() ).
Are you sure about that? getPolicy() just makes a security check and calls getPolicyNoCheck(). The code for the latter (in JDK 1.4.2 anyway) is basically

Looks like a singleton to me. Are you using a particular JDK where you see something different?
It's possible some other code is calling getPolicy() and storing the result somewhere, so that when you later setPolicy() the stored copy is unaffected. You could try something like this:

Then do something like

Later you can do dp.setCurrentPolicy(somethingElse), and all code that uses Policy.getPolicy() will end up invoking whatever you've set as current policy. Even if that code uses a cached reference to a Policy it obtained earlier - as long as you were able to install the DynamicPolicy as system policy before this caching occurred.
Robert Paris
Ranch Hand

Joined: Jul 28, 2002
Posts: 585
Originally posted by Jim Yingst:
Are you sure about that? getPolicy() just makes a security check and calls getPolicyNoCheck(). The code for the latter (in JDK 1.4.2 anyway) is basically

Looks like a singleton to me. Are you using a particular JDK where you see something different?

It's not a singleton because it instantiates the Policy object when the JVM starts up (that's one instance). Then when anyone else wants to get the policy object, I have to return an instance. How can I return the instance that was created by the JVM? I have no way to grab that instance.
I noticed this was a problem because when my test code called "getPolicy()" it returned a policy which (upon a check I realized) had the protectionDomain of the calling code. A doPrivileged call in that object failed because it was created by the app classloader not the bootclasspath classloader.
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
I misspoke when I called it a singleton; it's not a singleton because you can still create a new Policy eaily by calling the constructor. I'm not sure what the fact that this happens on JVM startup has to do with anything. You can easily have a singleton that's initialized when the class is first loaded. Or you can employ lazy instantiation instead, which is pretty common but not required. What I should have said was that getInstance() does not return a new instance every time it's called. In your test code, subsequent calls to getPolicy() should return the same instance. If it's not the instance you want, that seems to indicate that the original system policy has been replaced by the app classloader. It's not that you're creating new instances repeatedly, it's just that a new instance was created at some point, once, and the orignal instance is now inaccessible. I don't know how/if to get at a different policy; my guess is that this involves editing policy files. I mean, an application isn't supposed to be able to grant itself greater access than it was given by the system; that would defaut the whole point of having a security manager, wouldn't it? If you need a more permissive policy, I'd expect you'd have to set that it up from outside the application itself. But again, my knowledge of this area is fuzzy. I think I've reached the limit of my ability to help, as I don't have time to research this now.
Frank Carver
Sheriff

Joined: Jan 07, 1999
Posts: 6920
Just to add a bit more confusion to this, it's practically impossible to create a Singleton which is guaranteed across a whole VM. Strictly the techniques discussed above only give you a Singleton unique to a particular classloader context.
It's quite common in application servers, for example, to have many different classloader contexts in a single VM, and each could get a separate instance of your supposed Singleton.
I'm afraid I still don't quite understand why you feel you need a Singleton in this case, anyway. What's wrong with allowing an arbitrary number of such policy objects, but having them all refer to a centralised cache outside the policy object itself, sonething like:

Then all you need to do is call setCache() on any freshly created "lightweight" Policy object, however it has been created, and it will use the same cache as any others you have passed the same cache to.
The cache Map doesn't need to be a Singleton, either. You can just create one when your app starts up, and pass it in wherever needed.
Does that make sense, or have I missed the point?


Read about me at frankcarver.me ~ Raspberry Alpha Omega ~ Frank's Punchbarrel Blog
Robert Paris
Ranch Hand

Joined: Jul 28, 2002
Posts: 585
Jim, I'm not sure I'm understanding what you're referring to here.
What I should have said was that getInstance() does not return a new instance every time it's called.

The JRE code calls Class.forName().newInstance(), not "getInstance()". And as far as I understand it, newInstance() always returns a new instance.

Frank,
That (the classloader question) was one of my concerns, but I'm not sure it matters too much. The only way to influence the Policy is to work with the Policy object returned by getPolicy(). What I'm not understanding is, if someone calls getPolicy() and it returns a new object, how does modifying an instance variable change the installed policy when the AccessController will also be given a new instance?
In your example, I don't see how this problem is solved.
1. Someone calls getPolicy() and is given object (instance) "B".
2. They call setCache() on "B" and set the cache
3. The ACC calls getPolicyNoCheck() and gets the original instance which was created by the bootclasspath loader, object "A"
Am I correct in my assumption that the cache set on "B" is NOT accessible by "A"? In your code, it's not static, so how can the two instances access the same cache?
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
[Jim]: What I should have said was that getInstance() does not return a new instance every time it's called.
[Robert]: The JRE code calls Class.forName().newInstance(), not "getInstance()". And as far as I understand it, newInstance() always returns a new instance.

Oops. I meant getPolicy(), not getInstance(). You said that getPolicy() and getPolicyNoCheck() return a new instance each time; I'm saying no, they don't.
Robert Paris
Ranch Hand

Joined: Jul 28, 2002
Posts: 585
Jim - I'm sorry! You're right, I was looking at some messed up source code! Yikes. From what I see, it instantiates the Policy object and saves that to a static variable in java.security.Policy and then returns that every time. Thank you - problem solved!
(Is there an emoticon of someone hitting themself on the head?)
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
Is there an emoticon of someone hitting themself on the head?
Not currently. is sorta close. If you find a good icon for this that fits well with our others, let me know and I can install it here.
Robert Paris
Ranch Hand

Joined: Jul 28, 2002
Posts: 585
Ahh, here we go:
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: How make true Singleton, even if loaded by Class.forName?