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

Refactor extension condition #180

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
*
* @since 1.0.0
*/
@Target({ElementType.TYPE})
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright © ${year} ${owner} (${email})
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jd.live.agent.core.extension.annotation;

import java.lang.annotation.*;

/**
* An annotation used to mark an annotation type as a conditional composite.
*/
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConditionalComposite {

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ConditionalOnMissingClasses.class)
@Documented
@Conditional(dependOnLoader = true)
public @interface ConditionalOnMissingClass {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
*/
package com.jd.live.agent.core.extension.condition;

import lombok.Getter;

import java.lang.annotation.Annotation;
import java.util.List;

/**
* condition matcher
*/
Expand All @@ -28,4 +33,56 @@ public interface Condition {
*/
boolean match(ConditionContext context);

/**
* A condition that delegates the condition matching to a provided condition.
*
* @see Condition
* @see ConditionContext
*/
@Getter
class DelegateCondition implements Condition {

private final Annotation annotation;

private final Condition condition;

public DelegateCondition(Annotation annotation, Condition condition) {
this.annotation = annotation;
this.condition = condition;
}

@Override
public boolean match(ConditionContext context) {
return condition.match(context.getAnnotation() == annotation ? context : context.create(annotation));
}
}

/**
* A composite condition that checks if all of its sub-conditions are met.
* <p>
* This class represents a composite condition that consists of multiple sub-conditions. The condition is considered
* satisfied if and only if all of its sub-conditions are satisfied.
*
* @see Condition
* @see ConditionContext
*/
class CompositeCondition implements Condition {

private final List<? extends Condition> conditions;

public CompositeCondition(List<? extends Condition> conditions) {
this.conditions = conditions;
}

@Override
public boolean match(ConditionContext context) {
for (Condition condition : conditions) {
if (!condition.match(context)) {
return false;
}
}
return true;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@
import com.jd.live.agent.bootstrap.logger.Logger;
import com.jd.live.agent.bootstrap.logger.LoggerFactory;
import com.jd.live.agent.core.extension.annotation.Conditional;
import com.jd.live.agent.core.util.cache.LazyObject;
import com.jd.live.agent.core.extension.annotation.ConditionalComposite;
import com.jd.live.agent.core.extension.annotation.Extension;
import com.jd.live.agent.core.extension.condition.Condition.CompositeCondition;
import com.jd.live.agent.core.extension.condition.Condition.DelegateCondition;
import com.jd.live.agent.core.inject.annotation.Injectable;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
Expand All @@ -49,9 +54,9 @@ public class ConditionManager implements ConditionMatcher {

private static final String CONDITION_PACKAGE = ConditionManager.class.getPackage().getName();

private static final Map<Class<?>, LazyObject<Condition>> CONDITIONS = new ConcurrentHashMap<>();
private static final Map<Class<?>, Optional<Condition>> CONDITIONS = new ConcurrentHashMap<>();

private static final Map<Class<?>, List<ConditionalDesc>> TYPE_CONDITIONS = new ConcurrentHashMap<>();
private static final Map<Class<?>, List<DelegateCondition>> TYPE_CONDITIONS = new ConcurrentHashMap<>();

private final ClassLoader classLoader;

Expand Down Expand Up @@ -80,12 +85,13 @@ public boolean match(Class<?> type, ClassLoader classLoader, Predicate<Annotatio
if (type == null) {
return false;
}
List<ConditionalDesc> descs = TYPE_CONDITIONS.computeIfAbsent(type, this::getConditionals);
for (ConditionalDesc desc : descs) {
if (predicate == null || predicate.test(desc.annotation)) {
if (desc.condition == null || !desc.condition.match(
new ConditionContext(type, desc.annotation,
classLoader == null ? this.classLoader : classLoader, optional))) {
List<DelegateCondition> conditions = getConditions(type);
Annotation annotation;
for (DelegateCondition condition : conditions) {
annotation = condition.getAnnotation();
if (predicate == null || predicate.test(annotation)) {
if (annotation == null || !condition.match(new ConditionContext(
type, annotation, classLoader == null ? this.classLoader : classLoader, optional))) {
return false;
}
}
Expand All @@ -100,21 +106,79 @@ public boolean match(Class<?> type, ClassLoader classLoader, Predicate<Annotatio
* @param type The class type to retrieve conditions for.
* @return A list of conditional descriptors.
*/
private List<ConditionalDesc> getConditionals(Class<?> type) {
List<ConditionalDesc> result = new ArrayList<>();
private List<DelegateCondition> getConditions(Class<?> type) {
return TYPE_CONDITIONS.computeIfAbsent(type, this::parseConditions);
}

/**
* Returns a list of delegate conditions for the given type.
*
* @param type the type to retrieve delegate conditions for
* @return a list of delegate conditions for the given type
*/
private List<DelegateCondition> parseConditions(Class<?> type) {
List<DelegateCondition> result = new ArrayList<>();
Annotation[] annotations = type.getAnnotations();
for (Annotation annotation : annotations) {
Class<? extends Annotation> annotationType = annotation.annotationType();
Conditional conditional = annotationType.getAnnotation(Conditional.class);
if (conditional != null) {
LazyObject<Condition> lazyObject = CONDITIONS.computeIfAbsent(annotationType,
t -> new LazyObject<>(newImplement(conditional.value(), t.getSimpleName())));
result.add(new ConditionalDesc(annotation, lazyObject.get()));
Condition condition = getCondition(annotation.annotationType(), this::parseAnnotation);
if (condition != null) {
result.add(new DelegateCondition(annotation, condition));
}
}
return result;
}

/**
* Parses the given annotation type and returns a condition based on its conditional annotations.
*
* @param annotationType the annotation type to parse
* @return a condition based on the conditional annotations of the given annotation type, or null if the annotation
* type does not have a conditional annotation
*/
private Condition parseAnnotation(Class<?> annotationType) {
Conditional conditional = annotationType.getAnnotation(Conditional.class);
Condition condition = null;
if (conditional != null) {
condition = newImplement(conditional.value(), annotationType.getSimpleName());
} else {
ConditionalComposite composite = annotationType.getAnnotation(ConditionalComposite.class);
if (composite != null) {
condition = new CompositeCondition(parseConditions(annotationType));
}
}
return condition;
}

/**
* Returns a condition for the given annotation type.
* <p>
* This method uses a cache to store conditions for annotation types. If the condition for the given annotation type
* is not in the cache, it creates a new condition using the provided function and stores it in the cache. The
* function takes the annotation type as input and returns a new condition.
*
* @param type the annotation type to retrieve a condition for
* @param function a function that creates a new condition for the given annotation type
* @return a condition for the given annotation type, or null if the condition could not be created
*/
private Condition getCondition(Class<?> type, Function<Class<?>, Condition> function) {
if (type == Conditional.class
|| type == ConditionalComposite.class
|| type == Extension.class
|| type == Injectable.class
|| type.getName().startsWith("java.")) {
return null;
}
Optional<Condition> optional = CONDITIONS.get(type);
if (optional == null) {
optional = Optional.ofNullable(function.apply(type));
Optional<Condition> old = CONDITIONS.putIfAbsent(type, optional);
if (old != null) {
optional = old;
}
}
return optional.orElse(null);
}

/**
* Instantiates a new condition implementation based on the provided class name and conditional name. This method
* supports both explicit class names and a naming convention that infers the class name from the conditional
Expand Down Expand Up @@ -143,24 +207,4 @@ private Condition newImplement(String implementClass, String conditionalName) {
return null;
}

/**
* A private static class that serves as a container for holding a conditional annotation and its associated
* condition implementation. Each instance of this class represents a single conditional that is to be checked
* against a class type.
*
* <p>This class is used internally by the {@link ConditionManager} to cache and manage the relationship between
* annotations and their corresponding conditions, enabling efficient conditional matching.</p>
*/
private static class ConditionalDesc {

protected final Annotation annotation;

protected final Condition condition;

ConditionalDesc(Annotation annotation, Condition condition) {
this.annotation = annotation;
this.condition = condition;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright © ${year} ${owner} (${email})
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jd.live.agent.governance.annotation;

import com.jd.live.agent.core.extension.annotation.ConditionalComposite;
import com.jd.live.agent.core.extension.annotation.ConditionalOnProperty;
import com.jd.live.agent.governance.config.GovernanceConfig;

import java.lang.annotation.*;

/**
* An annotation used to mark a type as requiring the dubbo rpc feature to be enabled.
* <p>
* This annotation is used to indicate that a type requires the dubbo rpc feature to be enabled. The presence of this
* annotation on a type will trigger specific behavior in the governance processing logic.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ConditionalOnProperty(value = GovernanceConfig.CONFIG_LIVE_DUBBO_ENABLED, matchIfMissing = true)
@ConditionalComposite
public @interface ConditionalOnDubboEnabled {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright © ${year} ${owner} (${email})
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jd.live.agent.governance.annotation;

import com.jd.live.agent.core.extension.annotation.ConditionalComposite;
import com.jd.live.agent.core.extension.annotation.ConditionalOnProperty;
import com.jd.live.agent.governance.config.GovernanceConfig;

import java.lang.annotation.*;

/**
* An annotation used to mark a type as requiring the flow control feature to be enabled.
* <p>
* This annotation is used to indicate that a type requires the flow control feature to be enabled. The presence of this
* annotation on a type will trigger specific behavior in the governance processing logic.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ConditionalOnProperty(value = GovernanceConfig.CONFIG_FLOW_CONTROL_ENABLED, matchIfMissing = true)
@ConditionalComposite
public @interface ConditionalOnFlowControlEnabled {

}
Loading
Loading