• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 566
  • Last Modified:

Forcing object creation through a factory

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
DiamonDogX
Asked:
DiamonDogX
  • 8
  • 5
  • 5
  • +2
2 Solutions
 
valipotorCommented:
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
 
VenabiliCommented:
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
 
dbkrugerCommented:
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
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
VenabiliCommented:
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
 
dbkrugerCommented:
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
 
VenabiliCommented:
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
 
dbkrugerCommented:
> 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
 
VenabiliCommented:
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
 
valipotorCommented:
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
 
kannan_ekanathCommented:
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
 
DiamonDogXAuthor Commented:
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
 
VenabiliCommented:
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
 
dbkrugerCommented:
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
 
dbkrugerCommented:
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
 
kannan_ekanathCommented:
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
 
kannan_ekanathCommented:
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
 
DiamonDogXAuthor Commented:
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
 
dbkrugerCommented:
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
 
DiamonDogXAuthor Commented:
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
 
kannan_ekanathCommented:
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
 
DiamonDogXAuthor Commented:
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
 
dbkrugerCommented:
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
 
valipotorCommented:
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
 
DiamonDogXAuthor Commented:
I was just about to post the exact same thing valipotor :).  It definitely compiles, and definitely prints out "Can't call me? "
0
 
valipotorCommented:
Yes, it is a matter of security manager.

As kannan_ekanath said:

>> This consults the security manager. Your security managers *allows* it.
0
 
dbkrugerCommented:
Sorry, copied the wrong code of yours. The other one doesn't compile. My environment does stop you from doing this.
0

Featured Post

Important Lessons on Recovering from Petya

In their most recent webinar, Skyport Systems explores ways to isolate and protect critical databases to keep the core of your company safe from harm.

  • 8
  • 5
  • 5
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now