Skip to content

Proposal: More flexible CommitPropertiesProvider #1240

Description

@theorangeguo

Proposal: More flexible CommitPropertiesProvider

Context

Some cases need to generate commit Properties through the audited method.

The current solution is to extend Aspect to put the currently executed method information into ThreadLocal, but this is not a good practice.

Decision

  1. Add parameters(auditedMethod)

    Add parameter for the following methods of CommitPropertiesProvider
provideForCommittedObject(Method auditedMethod, Object domainObject)
provideForDeletedObject(Method auditedMethod, Object domainObject)
provideForDeleteById(Method auditedMethod, Class<?> domainObjectClass, Object domainObjectId)
  1. JaversCommitAdvice update
    Get auditedMethod from JoinPoint at JaversCommitAdvice call

    Example (pjp.signature as MethodSignature).method)

Consequences

After the client code is upgraded, it needs to be changed. It is best to migrate smoothly.

In order to ensure a smooth transition to the new calling method, we need to keep the old method and mark it as depredicate and wait for the old method to be deleted in subsequent versions

Activity

  1. bartoszwalacik commented on Mar 11, 2023

    @bartoszwalacik
    Member

    @orange-guo I like the idea of getting rid of ThreadLocal but the proposed solution it's not clear for me. How do you pass props? Could you provide more snippets to cover the full use case?

  2. theorangeguo commented on Mar 24, 2023

    @theorangeguo
    Author

    @bartoszwalacik

    Firstly, I would like to make some additions to the previous design. At the beginning of this proposal, I suggested adding an auditedMethod parameter. However, now I think we should not limit ourselves to just passing auditedMethod but instead pass an AuditingContext, it's an interface that provides the ability to read contextual information when the auditing action occurs, such as method, class, and other information. This approach will make it easier for us to extend in the future. Here is an example.

    public interface AuditingContext {
    
      public Method getMethod();
    
      public Class<?> getClass();
    
      // more additional method 
    
    }
    

    The following content is about the parameter passing you just mentioned. After reviewing the source code, I found that the current call to CommitPropertiesProvider to obtain commitProperties occurs in org.javers.spring.auditable.aspect.JaversCommitAdvice. This means that when calling, we need to create an AuditingContext. Currently, the AuditingContext contains information such as method and class, which can be provided by the JoinPoint passed when JaversCommitAdvice is called by JaversAuditableAspectAsync, JaversAuditableAspect, and JaversSpringDataAuditableRepositoryAspect

    Here is the snippets

    JaversAuditableAspectAsync.java

    @AfterReturning("@annotation(org.javers.spring.annotation.JaversAuditableAsync)")
    public void commitAdvice(JoinPoint pjp) {
      lastAsyncCommit = javersCommitAdvice.commitSaveMethodArgumentsAsync(pjp);
    }
    

    JaversCommitAdvice.java

    Optional<CompletableFuture<Commit>> commitSaveMethodArgumentsAsync(JoinPoint pjp) {
            List<CompletableFuture<Commit>> futures = AspectUtil.collectArguments(pjp)
                    .stream()
                    .map(arg -> commitObjectAsync(arg)) // 1. we need to pass the pjp to commitObjectAsync
                    .collect(Collectors.toList());
    
            return futures.size() == 0 ? Optional.empty() : Optional.of(futures.get(futures.size() - 1));
        }
    
        CompletableFuture<Commit> commitObjectAsync(Object domainObject) {
            String author = this.authorProvider.provide();
            // 2. we need to pass the pjp to propsForCommit
            return this.javers.commitAsync(author, domainObject, propsForCommit(domainObject), executor);
        }
    
    private Map<String, String> propsForCommit(Object domainObject) {
         // 3. We need to create the AuditingContext here, for performance we need to include the caching solution
            return Maps.merge(
                    commitPropertiesProvider.provideForCommittedObject(domainObject),
                    commitPropertiesProvider.provide());
        }
    

    Finally, based on my current understanding, the scope of impact mainly includes the following:

    • Adding AuditingContext
    • Adding a default implementation of AudiitngContext
    • Adding AuditingContextFactory responsible for creating AuditingContext
    • JaversCommitAdvice needs to add a field to include AuditingContextFactory
    • Adding parameters to CommitPropertiesProvider methods to ensure that AuditingContext can be passed
    • Refactoring JaversCommitAdvice to create AuditingContext using AuditingContextFactory and pass it to - CommitPropertiesProvider when calling commitPropertiesProvider.
  3. bartoszwalacik commented on Mar 31, 2023

    @bartoszwalacik
    Member

    But when you have a Method, you can easily get its class, by getDeclaringClass(), so the concept of AuditingContext seems redundant to me for now. I suggest to simplify this design and stick to bare Method, as auditing contect

  4. theorangeguo commented on Apr 3, 2023

    @theorangeguo
    Author

    My example may not be clear enough. Please consider the following situation: if we need to provide users with the parameters of the audited method at runtime in the future, we need to extend the existing parameter list. The advantage of using AuditingContext is that subsequent extensions will not affect user usage (it will not cause parameter list changes and cause compilation problems).

  5. bartoszwalacik commented on Jun 4, 2024

    @bartoszwalacik
    Member

    Hi @orange-guo , sorry for slow response. Okay , I appreciate the idea of AuditingContext. It can't be simply added to CommitPropertiesProvider without braking backward compatibility. So I guess a new bean type could be introduced, like AdvancedCommitPropertiesProvider. JaversCommitAdvice should support both beans

  6. theorangeguo commented on Jun 5, 2024

    @theorangeguo
    Author

    Hi @bartoszwalacik, It's nothing. I agree the new design is better than before modifying the JaversCommitAdvice.

    Changes

    If we decide to introduce it, we need to make the following changes:

    Code Implementation

    • Add the interface AdvancedCommitPropertiesProvider.
    • Add the interface AuditingContext and its implementation.
    • Inject AdvancedCommitPropertiesProvider into JaversCommitAdvice via Spring DI, and call it

    Docs

    • Add the introduction of AdvancedCommitPropertiesProvider into the documentation (usage).

    Mark CommitPropertiesProvider as deprecated?

    Should we consider marking it as deprecated (maybe deprecated is a better option)?

    • Add the @Deprecated annotation on JaversCommitAdvice and add reason of deprecated.

    These are my estimated changes. Please review them. If needed, I can request a PR to complete it.

  7. bartoszwalacik commented on Jun 5, 2024

    @bartoszwalacik
    Member

    Sounds good! Just a small correction : Add the @deprecated annotation on CommitPropertiesProvider and add reason of deprecated. If you contribute a PR I will merge it promptly. Thanks

  8. bartoszwalacik commented on Jun 19, 2024

    @bartoszwalacik
    Member

    Thanks for your contribution @orange-guo . I decided to change it a bit in order to reduce the impact on existing client's code. So the original CommitPropertiesProvider will stay untouched and won't be deprecated, the new interface (AdvancedCommitPropertiesProvider) extends the old one. So it's a kind of optional extension. I know that this approach (one interface extends another) is a bit unusual , but thanks to that we won't force client to change their code
    PTAL:
    https://github.com/javers/javers/pull/1390/files

  9. theorangeguo commented on Jun 23, 2024

    @theorangeguo
    Author

    @bartoszwalacik I have no problem with this change, it is the right decision to avoid forcing users to modify their code.

  10. added a commit that references this issue on Jun 23, 2024
  11. bartoszwalacik commented on Jun 23, 2024

    @bartoszwalacik
    Member
  12. added a commit that references this issue on Jun 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions