3

I was wondering if anyone had a pattern that would help me achieve the following:

We have a JPA entity called Employee and on it there is a setLineManager method. We also have a separate updateLineStructureService, which is a Spring-managed service bean. We want to try and ensure that this setLineManager method can only be called from updateLineStructureService and not directly from any other class.

Is there a way to allow the service access to this method without exposing it to any other classes? I am aware that I could give the method package level access and put the service in the same package as Employee, but that will not fit our package structure so I would prefer not to do that. I am also aware that I could make the method private and just access it through reflection in this one place, but I do not like that solution at all.

Any ideas?

DataNucleus
  • 15,497
  • 3
  • 32
  • 37
brent777
  • 3,369
  • 1
  • 26
  • 35
  • 6
    java is Java; you are stuck with the facilities it provides. Without heinous treachery, the answer is no. The package-level access idea doesn't make sense anyway, since anything else in the same package would also have access, not just your service. Any other class (assuming the classloader allows it) could also use reflection. – Dave Newton Jun 04 '12 at 17:03
  • Just an idea. You can make it protected and create an inner class extending Employee in your UpdateLineStructureService class. – toniedzwiedz Jun 04 '12 at 17:04
  • @Tom a protected mehtod can be seen by every other class in the package. – Pablo Jun 04 '12 at 17:05
  • @Pablo true, but it's still better than public... and still pretty ugly :/ – toniedzwiedz Jun 04 '12 at 17:07
  • See also: http://stackoverflow.com/questions/10784674 – Tomasz Nurkiewicz Jun 04 '12 at 19:25

6 Answers6

1

You can inspect the stacktrace (using Throwable#getStackTrace()) and see if it contains the allowed method on specified position.

npe
  • 15,395
  • 1
  • 56
  • 55
1

In the following code snippet, System.PrivateEmployee is not visible outside the System class. Thus effectively privateMethod is private and can only be called from within the System class. Since System.PrivateEmployee extends System.PublicEmployee it can be used outside the System class as System.PublicEmployee

public class System
{
      public static interface PublicEmployee { void publicMethod ( ) ; }
      private static interface PrivateEmployee extends PublicEmployee { void privateMethod ( ) ; }
}
emory
  • 10,725
  • 2
  • 30
  • 58
1

Use an inner class only available to the other service class:

public class Employee
{
  static {
    LineStructureService.registerEmployeeHelper(new EmployeeHelper() {
      @Override
      public void setLineManager(Employee emp, Object foo) {
        emp.setLineManager(foo);
      }
    });
  }

  public static void init() {}

  private void setLineManager(Object foo) { }  
}

public class LineStructureService
{
  private static volatile EmployeeHelper _helper;

  static {
    // ensure that Employee class is loaded and helper registered
    Employee.init();
  }

  public static synchronized void registerEmployeeHelper(EmployeeHelper helper) {
    _helper = helper;
  }

  public void doSomething(Employee emp)
  {
    // now this class can call setLineManager on Employee
    _helper.setLineManager(emp, blah);
  }

  public interface EmployeeHelper {
    public void setLineManager(Employee emp, Object foo);
  }
}
jtahlborn
  • 52,909
  • 5
  • 76
  • 118
0

The only way that a class can access private methods of other classes is with inner classes. If that is not an option, this can't be done.

Pablo
  • 3,655
  • 2
  • 30
  • 44
0

One approach is to make two forms of Employee.

"BasicEmployee" has all the methods except setLineManager(). "ExtendedEmployee" extends BasicEmployee and adds a public void setLineManager(). (I'm assuming these are classes, but they could also be interfaces instead) Underneath the hood, everything is really a FullEmployee (for clarity, you could make BasicEmployee abstract). But, in the code, in all the classes except UpdateLineStructureService, you declare it as a BasicEmployee. Only in UpdateLineStructureService is it declared as a FullEmployee. So, only UpdateLineStructureService has easy access to setLineManager()

Now, a rogue coder could always cast their BasicEmployee to an ExtendedEmployee to access setLineManager(), so this isn't totally secure. But it's a reasonable pattern to limit access.

user949300
  • 15,364
  • 7
  • 35
  • 66
0

You could use AOP (e.g. AspectJ or CDI) to intercept the call to setLineManager(); if the caller is updateLineStructureService() call the method; if not do nothing, or raise an exception or whatever.

mari
  • 234
  • 1
  • 5