Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provides a way for DecoratorFactory to be applied only once #5918

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

my4-dev
Copy link
Contributor

@my4-dev my4-dev commented Sep 18, 2024

Motivation:

Please refer to issue #5759

Modifications:

  • Refactored original logics of collecting decorator in order to remove side effects as much as possible.
  • Added addtivity flag to DecoratorFactory which makes us possible to control that the annotation that has the highest priority to be only applied within annotations annotaed by this DecoratorFactory.

Result:

@RequiresPermission(value = Permission.WRITE, order = 2)
static class SensitiveService {
    @RequiresPermission(value = Permission.READ, order = 1)
    @Get("/")
    public String get() { ... }

    @Post("/")
    public String create() { ... }

    @Put("/")
    public String update() { ... }
}

@DecoratorFactory(value = PermissionCheckerFactory.class, additivity = false)
@Retention(RetentionPolicy.RUNTIME)
@interface RequiresPermission {
    Permission value() default Permission.READ;
    int order() default 0;
}

enum Permission {
    READ, WRITE
}

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before going into code review, I think it's better if some points are clarified cc. @ikhoon (the original issue creator)

  1. Can repeatable be set for decorators also? It seems odd that only DecoratorFunctions only are considered. I think we can probably use the same trick that was used for order to determine additivity of decorators added by factories.

e.g.

@RequiresPermission(Permission.WRITE)
static class SensitiveService {
    @RequiresPermission(Permission.READ)
    @Decorator(PermissionDecorator.class)
    @Get("/")
    public String get() { ... }
}
  1. Additivity originally turns off the cumulative behavior. Do we following this rule? or do we just apply the highest order decorator if any one of the decorators have additivity=true?

e.g. Assume that decorator1(order=0), decorator2(order=50, additivity = false), decorator3(order=100). Then only decorator1, decorator2 will be applied following the original cumulativity rule.

@ikhoon
Copy link
Contributor

ikhoon commented Sep 20, 2024

Can repeatable be set for decorators also?

SGTM

or do we just apply the highest order decorator if any one of the decorators have additivity=true?

Did you mean additivity=false?

e.g. Assume that decorator1(order=0), decorator2(order=50, additivity = false), decorator3(order=100). Then only decorator1, decorator2 will be applied following the original cumulativity rule.

@RequiresPermission(Permission.WRITE)
static class SensitiveService {
    @RequiresPermission(Permission.READ)
    @Decorator(PermissionDecorator.class)
    @Get("/")
    public String get() { ... }
}

A class decorator has a lower priority (order) than a method decorator. IIUC, if we follow the rule, isn't @RequiresPermission(Permission.WRITE) chosen?

Initially, I intended to provide a way to apply the innermost decorator when the same decorators are applied repeatedly.

@jrhee17
Copy link
Contributor

jrhee17 commented Sep 20, 2024

A class decorator has a lower priority (order) than a method decorator. IIUC, if we follow the rule, isn't @RequiresPermission(Permission.WRITE) chosen?

I thought the order was determined by the order attribute value eventually

decorators.sort(Comparator.comparing(DecoratorAndOrder::order));

Initially, I intended to provide a way to apply the innermost decorator when the same decorators are applied repeatedly.

Perhaps I didn't understand the original intention of the repeatable field you proposed. So if repeatable=false is set for a decorator (decorator3) in the middle of decorators (decorator1~5), how does it affect which decorators are selected?

@ikhoon
Copy link
Contributor

ikhoon commented Sep 20, 2024

I thought the order was determined by the order attribute value eventually

I was talking about cases where the order is determined by the declared position of the decorator without explicitly declaring the order property.

// Class-level decorators are applied before method-level decorators.
collectDecorators(decorators, AnnotationUtil.getAllAnnotations(clazz));
collectDecorators(decorators, AnnotationUtil.getAllAnnotations(method));

So if repeatable=false is set for a decorator (decorator3) in the middle of decorators (decorator1~5), how does it affect which decorators are selected?

I thought about collecting decorators starting from the highest priority (innermost decorator) to the lowest priority until finding additivity = false. According to this rule, decorator3~5 will be applied.

@jrhee17
Copy link
Contributor

jrhee17 commented Sep 20, 2024

I thought about collecting decorators starting from the highest priority (innermost decorator)

To be clear, by priority do you mean order? I feel like it's more natural that the decorator application order also applies to additivity (or repeatable). Reversing this order feels counter-intuitive to me.

I think I'm out of ideas at the moment.

@ikhoon
Copy link
Contributor

ikhoon commented Sep 20, 2024

We have already introduced a simpler approach in MetricCollectingService in which the decorator application order is ignored. If multiple MetricCollectingServices are applied the last applied decorator is used.

// An inner `MetricCollectingService` takes precedence over an outer one. Delegate to the inner
// `MetricCollectingService` if exists.

So I didn't think it was odd.

Anyway, I also need to think more about the direction and API design.

@jrhee17
Copy link
Contributor

jrhee17 commented Sep 20, 2024

We have already introduced a simpler approach in MetricCollectingService in which the decorator application order is ignored. If multiple MetricCollectingServices are applied the last applied decorator is used.

I think there is a misunderstanding 😅 I'm not against ignoring the order of decorators as long as the change makes sense.

It seems like @ikhoon already has a good idea on how to proceed with this PR, so let me review this later after the PR is more developed 🙏

@my4-dev
Copy link
Contributor Author

my4-dev commented Sep 20, 2024

I'm sorry, I misundertood the requirement of this issue🙏
In my understanding, order simply means the order for decorators to be applied, but through the perspective of priority the decorator applied last is more prioritized than the others. (I implemented vice versa.)

But the requirement may be more complicated than I thought.

Anyway, I also need to think more about the direction and API design.

Thank you. Please let me know if they are fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provides a way for DecoratorFactory to be applied only once
3 participants