Create a protection agent using Java API (Head first design pattern)

Posted by travelbuff on Fri, 25 Feb 2022 10:51:58 +0100

Create a protection agent using Java API (Head first design pattern)

Java in Java Lang.reflect package has its own Proxy support. Using this package, we can dynamically create a Proxy class (Proxy) at runtime, implement one or more interfaces, and forward the call of the method to the class that implements the InvocationHandler interface. Because the actual agent class is created at runtime, we call this technology: dynamic agent.

We need to introduce a java proxy to protect the following requirements

Pairing of target villages

Assuming that every town needs matching service, we are responsible for helping the target village implement the dating matching management system. A good idea is to set up the evaluation of "Hot" and "Not", where "Hot" represents like and "Not" represents dislike.

Our service system first involves a Person Bean and creates a Person Bean interface

public interface PersonBean {

    String getName();
    String getGender();
    String getInterests();
    int getHotOrNotRating();

    void setName(String name);
    void setGender(String gender);
    void setInterests(String interests);
    void setHotOrNotRating(int rating);

}

Create the PersonBean implementation class PersonBeanImpl

public class PersonBeanImpl implements PersonBean{
    String name;
    String gender;
    String interests;
    int rating;
    int ratingCount = 0;
    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getGender() {
        return gender;
    }

    @Override
    public String getInterests() {
        return interests;
    }

    @Override
    public int getHotOrNotRating() {
        //Calculate your HotOrNotRating average
        if(ratingCount == 0) return 0;
        return rating/ratingCount;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public void setInterests(String interests) {
        this.interests = interests;
    }

    @Override
    public void setHotOrNotRating(int rating) {
        this.rating += rating;
        ratingCount++;
    }
}

Here, any user in the system can change other people's data at will. For example, I can modify other people's interests, gender, HotOrNotRating average value and so on at will. This is obviously unsafe, which may cause others to fail to date. Therefore, we need to set the system as the owner (oneself) to modify our own information. Non owners (others) can only affect their HotOrNotRating value. Here we need to use the protection agent - an agent that determines whether customers can access the object according to the access permission

To create this kind of proxy, we must use the dynamic proxy of Java API. java will create two proxies for us. We only need to provide handler to handle the transferred methods.

Step 1: create two invocationhandlers

InvocationHandler implements the behavior of proxy, and java is responsible for creating real proxy classes and objects. We only need to provide a handler that knows what to do when the method is called

Step 2: write code to create a dynamic proxy

We need to write some methods to generate Proxy instances. You'll see it later

Step 3: Test

Step 1: create two invocationhandlers

Whenever the method of the proxy object is called, the invoke() method in the InvocationHandler's implementation class will be called.

Create OwnerInvocationHandler (owner)

public class OwnerInvocationHandler implements InvocationHandler {
    PersonBean personBean;
    public OwnerInvocationHandler(PersonBean personBean){
        this.personBean = personBean;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("OwnerInvocationHandler->Called invoke method");
        try{-u jol,,
            if(method.getName().startsWith("get")){
                return method.invoke(personBean, args);
            }else if(method.getName().equals("setHotOrNotRating")){
                throw new IllegalAccessException();
            }else if(method.getName().startsWith("set")){
                return method.invoke(personBean, args);
            }
        }catch (InvocationTargetException invocationTargetException){
            invocationTargetException.printStackTrace();
        }
        return null;
    }
}

Create NonOwnerInvocationHandler (non owner)

public class NonOwnerInvocationHandler implements InvocationHandler {
    PersonBean personBean;
    public NonOwnerInvocationHandler(PersonBean personBean){
        this.personBean = personBean;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("NonOwnerInvocationHandler->Called invoke method");
        try{
            if(method.getName().startsWith("get")){
                return method.invoke(personBean, args);
            }else if(method.getName().equals("setHotOrNotRating")){
                return method.invoke(personBean, args);

            }else if(method.getName().startsWith("set")){
                throw new IllegalAccessException();
            }
        }catch (InvocationTargetException invocationTargetException){
            invocationTargetException.printStackTrace();
        }
        return null;
    }
}
Step 2: write code to create a dynamic proxy

Write methods to get instances of owner and non owner agents

    //Get my Proxy instance. Because Proxy and PersonBean implement the same interface, you can directly return the PersonBean object
    
  
    public static PersonBean getOwnerProxy(PersonBean person){
        return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(),
                person.getClass().getInterfaces(),
                new OwnerInvocationHandler(person));
    }

    //Get the non personal Proxy instance. Because Proxy and PersonBean implement the same interface, you can directly return the PersonBean object

    public static PersonBean getNotOwnerProxy(PersonBean person){
        return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(),
                person.getClass().getInterfaces(),
                new NonOwnerInvocationHandler(person));
    }
}

Step 3: Test

Create test class

public class Test {
    public static void main(String[] args) {
        PersonBean person = new PersonBeanImpl();
        PersonBean ownerProxy = getOwnerProxy(person);
        try {
            ownerProxy.setHotOrNotRating(100);
        } catch (Exception e) {
            System.out.println("You can't set a score for yourself~");
        }
        PersonBean notOwnerProxy = getNotOwnerProxy(person);
        try{
            notOwnerProxy.setGender("male");
        } catch (Exception e){
            System.out.println("Can't set other people's information~");
        }

    }
}

result

You can see that every time we call the method of the PersonBean instance at runtime, we pass the call to the corresponding implementation class that implements the InvocationHandler interface through the Proxy instance. The dynamic agent is realized. In the corresponding invoke() method, certain permission control is also carried out to control the access to the method, and the protection agent is preliminarily realized.

Topics: Java