Solved

Forcing object creation through a factory

Posted on 2006-10-30
29
558 Views
Last Modified: 2008-02-01
In Java, how does one force someone to call a factory in order to create an object of some type (i.e. MyFactory.createObjectA()) and have it so they cannot create an instance of that object without the factory?
0
Comment
Question by:DiamonDogX
  • 8
  • 5
  • 5
  • +2
29 Comments
 
LVL 6

Accepted Solution

by:
valipotor earned 25 total points
Comment Utility
You cannot force something like that.
Even if the constructor is private() the class can still be instantiated using introspection.

The coder must obey the rules of creating the object throgh a factory, in order for the app to be extended or patch without the need to recompile it.

0
 
LVL 20

Assisted Solution

by:Venabili
Venabili earned 25 total points
Comment Utility
Or if you do not trust the developer - make your constructor to determine which is the calling class and initialise only if it is the factory
This was working in 1.4.2
http://www.aptana.com/dev/index.php/Determining_the_calling_method_in_Java
I think there was a better way in Java 5, but let me try to find it

However - I would simply not do it... :) The Developer is supposed to use the thing in the way specified by the API...
0
 
LVL 11

Expert Comment

by:dbkruger
Comment Utility
If the constructor is private, then introspection will fail to acquire the method. You should be able to use that method to restrict who can call it.
0
 
LVL 20

Expert Comment

by:Venabili
Comment Utility
If the constructor is private, how the Factory will call it? Because from what I read here, The Asker needs a second class to create objects from his class... :)
0
 
LVL 11

Expert Comment

by:dbkruger
Comment Utility
public class TestIntrospect {
      public static void main(String args[]) {
            Class c = Foo.class;
            try {
                  Object x = c.newInstance();
            } catch (Exception e) {
                  System.out.println(e);
            }
      }

}
class Foo {
      private Foo() { System.out.println("Can't call me!"); }
}


java.lang.IllegalAccessException: Class TestIntrospect can not access a member of class Foo with modifiers "private"
0
 
LVL 20

Expert Comment

by:Venabili
Comment Utility
Yep - the private constructor cannot be called from outside at all - this is for sure. But the Asker needs a Factory class to call it. :)
0
 
LVL 11

Expert Comment

by:dbkruger
Comment Utility
> If the constructor is private, how the Factory will call it? Because from what I read here, The Asker needs a second class to create objects from his class... :)

In C++ you would use a friend. In Java, since that's not available, you use an inner class.

public class NotToBeInstantiatedByItself {
  private NotToBeInstantiatedByItself( ... ) { .. }


  public Factory gimmeAFactory() { ..}

  public static class FooFactory extends Factory {

  }
}
0
 
LVL 20

Expert Comment

by:Venabili
Comment Utility
The problem is that the Asker do not ask for inner classes. :) He asks for 2 separate classes...
The inner classes are good solution. To some extent... But not the perfect one.

Basically Java do not have the friends concept - sometimes I really miss it. Yes - you can go very close but not really to make it the same...

Protected constructor for class A and Factory class in the same package is what I usually use in such cases. And good documentation explaining how to create objects of the class. Usually works.
0
 
LVL 6

Expert Comment

by:valipotor
Comment Utility
To dbkruger & Venabili:

I wanted to say that a class can be instantiated even if the constructor was made private.

For example:


 Class a = A.class;
      try {
         Constructor contst = a.getDeclaredConstructor();
         contst.setAccessible(true);
         contst.newInstance();
      } catch (IllegalArgumentException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (InstantiationException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (IllegalAccessException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (InvocationTargetException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (SecurityException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } catch (NoSuchMethodException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }

where class a is defined :

public class A {

   private A()
   {
      System.out.println("prevent instantiation");
   }
}
0
 
LVL 5

Expert Comment

by:kannan_ekanath
Comment Utility
valipotor

Your argument is sorta wrong.
 contst.setAccessible(true);

consults the JVM security manager to check if private access of fields is allowed. However in typical production environment or for that matter in most app-servers, these flags are set to the level of security where private accesses of field will result in IllegalAccessException
0
 

Author Comment

by:DiamonDogX
Comment Utility
So basically the closest solution is to put the factory and the object you want to instantiate via the factory (with a protected constructor) in the same package and then just call the factory method from anywhere... ?
0
 
LVL 20

Expert Comment

by:Venabili
Comment Utility
Yep.
Or in the constructor of class A check who is the calling class and method and do something based on this. But I personally had never really done this :)
0
 
LVL 11

Expert Comment

by:dbkruger
Comment Utility
I would put it in the class wth a private constructor, as my example showed. If you put it in the package, anyone can write a class in that package and you have allowed them access.

If you use protected, any child could get it as well. If we could all declare who our parents are, Donald Trump and Queen Elizabeth would probably have a lot of self-declared "children" hoping to inherit.
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 11

Expert Comment

by:dbkruger
Comment Utility
Venabili --
You don't need to write the class as an inner class, you only need a tiny wrapper which instantiates the object in question and calls the generic factory.
But I think my post above highlights why you cannot sensibly use protected....
0
 
LVL 5

Expert Comment

by:kannan_ekanath
Comment Utility
A factory is no different from any other java class. In fact you are hitting one of the design patterns called "Singleton Pattern"

http://www.fluffycat.com/Java-Design-Patterns/Factory-Method/

Let me suggest smething I found best. Integrate the Factory class into the same class; For example,

class SomeImportantObject {
     SomeImportantObject instance = new SomeImportantObject();

     private SomeImportantObject() {
        // no one other than this class can use it
     }

     public static SomeImportantObject getSingletonObject() {
          return instance;
     }
}
0
 
LVL 5

Expert Comment

by:kannan_ekanath
Comment Utility
The above is something i found best, since most design experts would call "Singleton pattern" as somethign sort of "Anti pattern"

To put the argument in a different way, a singleton pattern if used badly beats the purpose of "Data encapsulation". If you have a class like above then it is equivalent to having a *DECLARED GLOBAL OBJECT* in C, C++ etc

Imagine, anyone can take that importantobject any time they want, call methods on it....

If your application needs it, then use it but use it sparingly :)
0
 

Author Comment

by:DiamonDogX
Comment Utility
What seems to break encapsulation is reflection.  I am able to call private constructors and private methods of a class from outside of it!  It feels like hacking... I can call private methods in the java API classes through reflection as well.  Is there anything I'm missing?  Doesn't this type of thing seem vulnerable?
0
 
LVL 11

Expert Comment

by:dbkruger
Comment Utility
I don't know how your permissions are set, but as I showed above, I could  not break encapsulation. If you have the code permissions set up properly, which they are by default, this should not happen. Reflection does not get you code that you could not otherwise access.
0
 

Author Comment

by:DiamonDogX
Comment Utility
But I can set them to whatever I want... therefore, I can call the private methods...

Class c = MyClass.class;
Method m = c.getDeclaredMethods[ 0 ];
m.settAccessible( true );
m.invoke( null, null );
0
 
LVL 5

Expert Comment

by:kannan_ekanath
Comment Utility
m.settAccessible( true );

This consults the security manager. Your security managers *allows* it. That was my previous comment :)

check the source code here,
http://docjar.com/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible(boolean)

It consults reflect permission. Google it if you want to know how to turn it on/off
0
 

Author Comment

by:DiamonDogX
Comment Utility
I understand that this concerns the security manager... my point is that I CAN turn this on or off and invoke private methods.  How does this work with the Java API classes?  I listed the methods for GridBagLayout.class for example and I saw a private method called removeConstraints() that I was able to invoke.  What else am I missing here?  Obviously the Java API classes are secure...
0
 
LVL 11

Expert Comment

by:dbkruger
Comment Utility
Your code obviously cannot even compile, so I suggest you compile it before claiming it works.
A constructor is not a method
You have to have an object in order to invoke a method on it

Your code is below. I fixed it and tried to get the constructor, and was told it did not exist, the exception comes on the getConstructor method, so you don't even have the opportunity to try to set it.


>Class c = MyClass.class;
>Method m = c.getDeclaredMethods[ 0 ]; // WRONG
>m.settAccessible( true );
>m.invoke( null, null );

Correct (but fails on getConstructor)

import java.lang.reflect.*;


public class TestIntrospect {
      public static void main(String args[]) {
            Class c = Foo.class;
            try {
                  //Object x = c.newInstance();
                  Class[] parameterTypes = new Class[0];
                  Constructor con = c.getConstructor(parameterTypes);
                  con.setAccessible( true );
                  Object[] empty = new Object[0];
                  Object f = con.newInstance(empty);

            } catch (Exception e) {
                  System.out.println(e);
            }
      }

}
class Foo {
      private Foo() { System.out.println("Can't call me!"); }
}
0
 
LVL 6

Expert Comment

by:valipotor
Comment Utility
As I have already posted:

Class a = A.class;
      try {
         Constructor contst = a.getDeclaredConstructor();
         contst.setAccessible(true);
         contst.newInstance();
      } catch (Throwable e) {
         e.printStackTrace();
      }

where class a is defined :

public class A {

   private A()
   {
      System.out.println("Can't call me? ");
   }
}
0
 

Author Comment

by:DiamonDogX
Comment Utility
I was just about to post the exact same thing valipotor :).  It definitely compiles, and definitely prints out "Can't call me? "
0
 
LVL 6

Expert Comment

by:valipotor
Comment Utility
Yes, it is a matter of security manager.

As kannan_ekanath said:

>> This consults the security manager. Your security managers *allows* it.
0
 
LVL 11

Expert Comment

by:dbkruger
Comment Utility
Sorry, copied the wrong code of yours. The other one doesn't compile. My environment does stop you from doing this.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
copyEndy  challenge 15 54
How Complex Is This Java Course ? 9 62
word0 challenge 4 52
Problem to start Neon 20 48
Java had always been an easily readable and understandable language.  Some relatively recent changes in the language seem to be changing this pretty fast, and anyone that had not seen any Java code for the last 5 years will possibly have issues unde…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
This tutorial will introduce the viewer to VisualVM for the Java platform application. This video explains an example program and covers the Overview, Monitor, and Heap Dump tabs.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now