0

I've got the following example class:

public class MyPermission implements Permission {
    public static final String READ = "read";
    public static final String UPDATE = "update";
    public static final String DELETE = "delete";

    @Override
    public boolean isGranted(String permission) {
        switch(permission) {
        case READ: return read;
        case UPDATE: return update;
        case DELETE: return delete;
        default: return false;
        }
    }

    private boolean read;
    public boolean isRead() { return read; }
    public void setRead(boolean read) { this.read = read; }

    private boolean update;
    public boolean isUpdate() { return update; }
    public void setUpdate(boolean update) { this.update = update; }

    private boolean delete;
    public boolean isDelete() { return delete; }
    public void setDelete(boolean delete) { this.delete = delete; }
}

And I want to simplify things a bit, because there will be created much more of these classes. The schema is always the same:

  • some public final static String Permissions (must be accessible inside Annotations)
  • each of those Permissions has a corresponding boolean field
  • method isGranted returns the value of the corresponding boolean field

As you can see in the example code: I've go to write a lot of code, to achieve this and I can't figure out how to simplify things.

There are 2 things I could imagine:

  1. call super(READ, UPDATE, DELETE); inside the constructor and let the super class handle the isGranted(...) method via reflection.

  2. just call super(); inside the constructor and the class will find the public static final String fields itself and create the fields and getter/setter dynamically, because I don't need them in my code - they just have to be there at runtime.

Or is there any cool new feature in Java 8, so I can do this:

public MyPermission() {
  super(new HashMap<String, GetterMethod>() {{
    put(READ, this::isRead);
    put(UPDATE, this::isUpdate);
    put(DELETE, this::isDelete);
  }});
}

So I could dynamically invoke the corresponding getter method, like:

public boolean isGranted(String permission) {
  return permissionMap.get(permission).execute();
}

(or even using the field, instead of the getter method)

It would be cool, if there's a simple and nice solution for this :)

Thanks in advance!

Benjamin M
  • 23,599
  • 32
  • 121
  • 201
  • 3
    Why aren't you using enums? – chrylis -cautiouslyoptimistic- Jul 14 '14 at 01:17
  • Do you explicitly need the setter and getters for each of the `boolean` variables? – Jack Jul 14 '14 at 01:18
  • There are 2 reasons: `1.`: This is some kind of Entity class for Neo4j. `2.` I must be able to use those String inside an annotation, and `@Annotation(MyEnum.READ.toString())` is not possible. – Benjamin M Jul 14 '14 at 01:19
  • I don't think I need getter and setters, it's just a habit and in this scenario it's not necessary, because those `boolean` fields are just there for the entity manager, I don't use them in my code directly. (There's also a `setPermission` method, which I omitted, because it's basically the same as the `isGranted` method) – Benjamin M Jul 14 '14 at 01:21
  • A related article on [accessing beans and properties through expressions](http://ugate.wordpress.com/2012/06/06/javafx-pojo-bindings/). Some are nervous to make their class members public and not have the bean get/set protection. When debugging complex systems its difficult to know who/what/where/when is the cause of a value change. When going public, be careful of code smell. – javajon Jul 14 '14 at 01:31
  • Thank you, I'll have a look at your link. `...` looks like they also use reflection, just like Jackson does with its *java field name -> json property* converter. So I could basically just stay with a simple method/field access via reflection solution. Or have I missed something in the article? – Benjamin M Jul 14 '14 at 01:41

2 Answers2

0

How about using Java 8 @FunctionalInterface Supplier and @FunctionalInterface Consumer?

public abstract class Permission {
    Map<String, Supplier<Boolean>> permissionGetterMap = new HashMap<>();
    Map<String, Consumer<Boolean>> permissionSetterMap = new HashMap<>();

    public void put(String permission, Supplier<Boolean> getter, Consumer<Boolean> setter) {
        permissionGetterMap.put(permission, getter);
        permissionSetterMap.put(permission, setter);
    }

    public boolean isGranted(String permission) {
        return permissionGetterMap.get(permission).get();
    }

    public void setPermission(String permission, boolean granted) {
        permissionSetterMap.get(permission).accept(granted);
    }
}



public class MyPermission extends Permission {
    public static final String READ = "read";
    public static final String UPDATE = "update";
    public static final String DELETE = "delete";

    public MyPermission() {
        put(READ, this::isRead, this::setRead);
        put(UPDATE, this::isUpdate, this::setUpdate);
        put(DELETE, this::isDelete, this::setDelete);
    }

    private boolean read;
    public boolean isRead() { return read; }
    public void setRead(boolean read) { this.read = read; }

    private boolean update;
    public boolean isUpdate() { return update; }
    public void setUpdate(boolean update) { this.update = update; }

    private boolean delete;
    public boolean isDelete() { return delete; }
    public void setDelete(boolean delete) { this.delete = delete; }
}

This does at least save some code, but:

  1. it's not much shorter than the version above, and
  2. those 2 Maps will generate some overhead. (could be important, since those Permission objects will get created about 100-1000 times per second when the application is live)
Benjamin M
  • 23,599
  • 32
  • 121
  • 201
  • If you don't need the `is` and `set` for each boolean you could just access them directly `put(READ, () -> read, b -> read = b)`, I don't see how you would be able to get any shorter than that, unless you can put your booleans in an array. – Alex - GlassEditor.com Jul 14 '14 at 02:31
  • This `Permission` will get saved to DB, so I need the key (like `READ`) and the value. Unfortunately my DB (Neo4j) doesn't support `Map`s, else I'd go for that solution. ... Do you have any idea how to get rid of those 2 `Map`s in my impl? That's the last thing that's annoying concerning that solution. – Benjamin M Jul 14 '14 at 02:36
0

I'd leave Java8 alone and just head for design patterns, strategy to be exact.

Either you can derive a subclass per each case or you can introduce an enum. Both are type safe, testable and you're not building another String centric application.

So you either end up with classes like

WritePermission extends BasePermission...
ReadPermission extends BasePermission...
BasePermission implements Permission...

Or with something like

public enum Permissions implements Permission {
  WRITE {public boolean isWrite{return true;}},
  READ {...}, 
  ...
  public boolean isWrite{return false;}
}

Or while you're with enums, you could resign from boolean methods at all, as you can just start comparing persmissions with enum (but that does not scale well, if you're going to do a hierarchies of permissions (e.g. ReadWrite). In that case classes are better

Michal Gruca
  • 522
  • 3
  • 6
  • Thanks for your answer. 1st problem is: I need static final Strings, because I need to use them inside annotations, i.e. `@PreAuthorize("hasPermission("+MyPermission.READ+")")`. 2nd problem: Those Permission classes will get persisted, though it must be some kind of POJO. ... The question is more about DRY ( http://en.wikipedia.org/wiki/Don't_repeat_yourself ): I have write 20 lines of code, which is pretty much like 4 lines with changing field names. – Benjamin M Jul 14 '14 at 22:02
  • Enums are great for persisting, and you can use them with toString. Same with strategy, and new classes, just override toString or add public field. Depends on what you need :) – Michal Gruca Jul 15 '14 at 18:44