Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice to prevent further instantiation of java classes

I have some class storing keys with important information. No one else is allowed to create a key, since a key relys on static information (like certain directory structures etc.).

public final class KeyConstants
{

    private KeyConstants()
    {
        // could throw an exception to prevent instantiation
    }

    public static final Key<MyClass> MY_CLASS_DATA = new Key<MyClass>("someId", MyClass.class);

    public static class Key<T>
    {
        public final String ID;
        public final Class<T> CLAZZ;

        private Key(String id, Class<T> clazz)
        {
            this.ID = id;
            this.CLAZZ = clazz;
        }
    }

}

This example is simplyfied.

I wanted to test the consequences of a wrong key (exception handling, etc.) and instantiated the class via reflection in a JUnit test case.

Constructor<?> c = KeyConstants.Key.class.getDeclaredConstructor(String.class, Class.class);
c.setAccessible(true);
@SuppressWarnings ("unchecked")
KeyConstants.Key<MyClass> r = (KeyConstants.Key<MyClass>) c.newInstance("wrongId", MyClass.class);

Then I asked myself how could I prevent further instantiation of the key class (i. e. preventing further object creating via reflection)?

enums came to my mind, but they don't work with generics.

public enum Key<T>
{
    //... Syntax error, enum declaration cannot have type parameters
}

So how can I keep a set of n instances of a generic class and prevent further instantiation?

like image 246
mike Avatar asked Aug 01 '13 13:08

mike


4 Answers

So how can I keep a set of n instances of a generic class and prevent further instantiation?

If you truly want to use this pattern, then no one (including you) should be able to instantiate a Key object. In order to keep a set of n instances in a class with this pattern, you could have a private constructor, a static method for access and a SecurityManager to prevent reflection. And since you want to be able to access the keys as pubic constants, I would try something like this..

public class KeyConstants{

    // Here are your n instances for public access
    public static final int KEY_1 = 1;
    public static final int KEY_2 = 2;
    .
    .
    .
    public static final int KEY_N = 'n';


    // now you can call this method like this..
    // Key mKey = KeyConstants.getKey(KeyConstants.KEY_1);
    public static Key getKey(int key){

         List keys = Key.getInstances();

         switch(key){

         case KEY_1:
                     return keys.get(0);
         case KEY_2:
                     return keys.get(1);
         .
         .
         .
         case KEY_N:
                     return keys.get(n);
         default:
                     // not index out of bounds.. this means
                     // they didn't use a constant
                     throw new IllegalArgumentException();
         }

    }

    static class Key<T>{
        private static List<Key> instances;
        private String ID;
        private Class<T> CLAZZ;

        private Key(String id, Class<T> clazz){
                      this.ID = id;
                      this.CLAZZ = clazz;
         }

        public static List<Key> getInstances(){
            if(instances == null){

                            instances = new ArrayList<Key>();
                //populate instances list
            }

                    return instances;
        }
    }
}

Use SecurityManager to prevent reflection access.

//attempt to set your own security manager to prevent reflection
    try {
        System.setSecurityManager(new MySecurityManager());
    } catch (SecurityException se) { 
    }

class MySecurityManager extends SecurityManager {

    public void checkPermission(Permission perm) {
        if (perm.getName().equals("suppressAccessChecks"))
            throw new SecurityException("Invalid Access");
    }

}

This will throw a SecurityException anytime someone attempts to access a private variable or field in your class (including access attempts via reflection).

like image 94
14 revs, 2 users 96% Avatar answered Oct 14 '22 00:10

14 revs, 2 users 96%


I'm not sure I fully understand your question, but if a private constructor is not sufficient, can you use a more dynamic approach and throw an exception in the constructor after a signal is given? For example:

public static class Key<T>
{
  private static boolean isLocked = false;

  // Call this method when you want no more keys to be created
  public static void lock() { isLocked = true; }

  ...

      private Key(String id, Class<T> clazz)
      {
          if (isLocked) throw new IllegalStateException("Cannot create instances of Key");
          this.ID = id;
          this.CLAZZ = clazz;
      }
}

Then - and this is the disadvantage - you will have to call Key.lock() once you want to prevent more instances being created.

like image 21
devrobf Avatar answered Oct 14 '22 00:10

devrobf


As you showed in your code to prevent instantiating KeyConstants you can throw some Exception inside private-non-argument constructor.

Harder part is way to block creating KeyConstants.Key constructor from outside of KeyConstants class.

Some wild idea

Maybe create Exception in your constructor and check how its stack trace looks like. When I add this code to constructor

private Key(String id, Class<T> clazz) {

    StackTraceElement[] stack = new Exception().getStackTrace();
    for (int i=0; i<stack.length; i++){
        System.out.println(i+") "+stack[i]);
    }

    this.ID = id;
    this.CLAZZ = clazz;
}

and create instance of Key with reflection like

Constructor<?> c = KeyConstants.Key.class.getDeclaredConstructor(
        String.class, Class.class);
c.setAccessible(true);
KeyConstants.Key<MyClass> r = (KeyConstants.Key<MyClass>) c
        .newInstance("wrongId", MyClass.class);

I get

0) KeyConstants$Key.<init>(Test.java:38)
1) sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
2) sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
3) sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
4) java.lang.reflect.Constructor.newInstance(Constructor.java:525)

so maybe just if 4th element of stack is java.lang.reflect.Constructor.newInstance throw Exception to prevent executing rest of constructors code like:

if (stack.length>=4 && stack[4].toString().startsWith("java.lang.reflect.Constructor.newInstance")){
    throw new RuntimeException("cant create object with reflection");
}
like image 25
Pshemo Avatar answered Oct 14 '22 00:10

Pshemo


I have another approach, you can bound an interface in a way to only be implemented by enum. With that approach you have a fixed set of instances at compile time.

If you want to add lazy loading, the enums implementing it should be proxies that load the desired object if it is requested. The class or classes that are hidden behind the proxies should only be visible to them, so that they have exclusive access to the constructor.

public class User {

  public static <S> S handleKey(FixedInstanceSet<S,?> key) {
    return key.getKey();
  }
}

interface FixedInstanceSet<S, T extends Enum<T> & FixedInstanceSet<S,T>>
{
  public S getKey();
}

enum StringKeys implements FixedInstanceSet<String, StringKeys> {
  TOP, DOWN, LEFT, RIGHT;
  @Override
  public String getKey() { return null; }
}

enum IntKeys implements FixedInstanceSet<Integer, IntKeys > {
  TOP, DOWN, LEFT, RIGHT;
  @Override
  public Integer getKey() { return null; }
}

/*
 * Bound mismatch: The type NotWorking is not a valid substitute for the bounded
 * parameter <T extends Enum<T> & FixedInstanceSet<S,T>> of the type
 * FixedInstanceSet<S,T>
 */
//class NotCompiling implements FixedInstanceSet<String, NotCompiling> {
//
//  @Override
//  public String getKey() { return null; }
//}
like image 1
mike Avatar answered Oct 14 '22 01:10

mike