Repository: deltaspike Updated Branches: refs/heads/master 028ec5bf3 -> 3734100fe
DELTASPIKE-1316 add InterDynExtension this allows to configurably add annotations to classes identified via a regexp. Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/2c185bd5 Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/2c185bd5 Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/2c185bd5 Branch: refs/heads/master Commit: 2c185bd5f02ea842b9d2f4628a7204c0453eec0d Parents: 028ec5b Author: Mark Struberg <strub...@apache.org> Authored: Fri Feb 9 18:31:33 2018 +0100 Committer: Mark Struberg <strub...@apache.org> Committed: Fri Feb 9 18:31:33 2018 +0100 ---------------------------------------------------------------------- .../core/api/config/base/CoreBaseConfig.java | 31 ++++ .../interceptor/interdyn/AnnotationRule.java | 55 +++++++ .../interceptor/interdyn/InterDynExtension.java | 161 +++++++++++++++++++ 3 files changed, 247 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/deltaspike/blob/2c185bd5/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/base/CoreBaseConfig.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/base/CoreBaseConfig.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/base/CoreBaseConfig.java index befd31e..02312f4 100644 --- a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/base/CoreBaseConfig.java +++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/base/CoreBaseConfig.java @@ -110,4 +110,35 @@ public interface CoreBaseConfig extends DeltaSpikeBaseConfig .withDefault(Boolean.FALSE) .getValue(); } + + interface InterDynCustomization + { + /** + * All interdyn rules start with this prefix and contains a 'match' and a 'annotation' part. + * The 'match' is a regular expression which depicts the classes which should get annotated. + * The 'annotation' is the annotation name which should get applied to all the classes which + * match the 'match' regexp. + * + * A sample config might look like: + * <pre> + * deltaspike.interdyn.rule.1.match=com\.mycorp\..*Service.* + * deltaspike.interdyn.rule.1.annotation=org.apache.deltaspike.core.api.monitor.InvocationMonitored + * </pre> + */ + String INTERDYN_RULE_PREFIX = "deltaspike.interdyn.rule."; + + /** + * Whether the InterDyn feature is enabled or not. + * + * If the feature is enabled at startup then we will apply the interceptors dynamically + * to all the matching classes. + * Otherwise we will skip the instrumentation. + */ + ConfigResolver.TypedResolver<Boolean> INTERDYN_ENABLED = + ConfigResolver.resolve("deltaspike.interdyn.enabled") + .as(Boolean.class) + .withCurrentProjectStage(true) + .withDefault(Boolean.FALSE); + + } } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/2c185bd5/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/interceptor/interdyn/AnnotationRule.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/interceptor/interdyn/AnnotationRule.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/interceptor/interdyn/AnnotationRule.java new file mode 100644 index 0000000..a93177b --- /dev/null +++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/interceptor/interdyn/AnnotationRule.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.deltaspike.core.impl.interceptor.interdyn; + + +import java.lang.annotation.Annotation; + +/** + * Contains a mapping between a dynamic interceptor rule and the name of the additional annotation to be added + */ + +public class AnnotationRule +{ + /** + * A RegExp to identify the classes which should get modified + */ + private String rule; + + /** + * The Annotation to be added + */ + private Annotation additionalAnnotation; + + public AnnotationRule(String rule, Annotation interceptorBinding) + { + this.rule = rule; + this.additionalAnnotation = interceptorBinding; + } + + public String getRule() + { + return rule; + } + + public Annotation getAdditionalAnnotation() + { + return additionalAnnotation; + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/2c185bd5/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/interceptor/interdyn/InterDynExtension.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/interceptor/interdyn/InterDynExtension.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/interceptor/interdyn/InterDynExtension.java new file mode 100644 index 0000000..61a60e7 --- /dev/null +++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/interceptor/interdyn/InterDynExtension.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.deltaspike.core.impl.interceptor.interdyn; + + +import org.apache.deltaspike.core.api.config.ConfigResolver; +import org.apache.deltaspike.core.api.config.base.CoreBaseConfig; +import org.apache.deltaspike.core.spi.activation.Deactivatable; +import org.apache.deltaspike.core.util.ClassDeactivationUtils; +import org.apache.deltaspike.core.util.ClassUtils; +import org.apache.deltaspike.core.util.metadata.AnnotationInstanceProvider; +import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.BeforeBeanDiscovery; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.ProcessAnnotatedType; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +/** + * <p>InterDyn is a CDI (JSR-299) Extension for dynamically + * attaching annotations (e.g. CDI interceptors) to a class.</p> + * + */ +public class InterDynExtension implements Deactivatable, Extension +{ + private List<AnnotationRule> interceptorRules = new ArrayList<AnnotationRule>(); + + + private Logger logger = Logger.getLogger(InterDynExtension.class.getName()); + + private Map<String, Annotation> usedInterceptorBindings = new HashMap<String, Annotation>(); + + private boolean enabled = false; + + @SuppressWarnings("UnusedDeclaration") + protected void init(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) + { + if (!ClassDeactivationUtils.isActivated(getClass())) + { + return; + } + + enabled = CoreBaseConfig.InterDynCustomization.INTERDYN_ENABLED.getValue(); + + if (enabled) + { + logger.info("Starting with deltaspike.interdyn instrumentation"); + init(); + } + } + + public void init() + { + Set<String> ruleConfigKeys = new HashSet<String>(); + + // first we collect all the rule property names + for (String propertyName : ConfigResolver.getAllProperties().keySet()) + { + if (propertyName.startsWith(CoreBaseConfig.InterDynCustomization.INTERDYN_RULE_PREFIX) && + propertyName.contains(".match")) + { + ruleConfigKeys.add(propertyName.substring(0, propertyName.indexOf(".match"))); + } + } + + for (String ruleConfigKey : ruleConfigKeys) + { + String match = ConfigResolver.getPropertyValue(ruleConfigKey + ".match"); + String annotationClassName = ConfigResolver.getPropertyValue(ruleConfigKey + ".annotation"); + + if (match != null && annotationClassName != null) + { + Annotation anno = getAnnotationImplementation(annotationClassName); + interceptorRules.add(new AnnotationRule(match, anno)); + } + } + + + if (interceptorRules.isEmpty()) + { + enabled = false; + } + } + + public void processAnnotatedType(@Observes ProcessAnnotatedType pat) + { + if (enabled) + { + String beanClassName = pat.getAnnotatedType().getJavaClass().getName(); + AnnotatedType at = pat.getAnnotatedType(); + AnnotatedTypeBuilder atb = null; + for (AnnotationRule rule : interceptorRules) + { + if (beanClassName.matches(rule.getRule())) + { + if (atb == null) + { + atb = new AnnotatedTypeBuilder(); + atb.readFromType(at); + } + atb.addToClass(rule.getAdditionalAnnotation()); + logger.info("Adding Dynamic Interceptor " + rule.getAdditionalAnnotation() + + " to class " + beanClassName ); + } + } + if (atb != null) + { + pat.setAnnotatedType(atb.create()); + } + } + } + + private Annotation getAnnotationImplementation(String interceptorBindingClassName) + { + Annotation ann = usedInterceptorBindings.get(interceptorBindingClassName); + + if (ann == null) + { + Class<? extends Annotation> annClass; + try + { + annClass = (Class<? extends Annotation>) + ClassUtils.getClassLoader(null).loadClass(interceptorBindingClassName); + } + catch (ClassNotFoundException e) + { + throw new RuntimeException("Error while picking up dynamic InterceptorBindingType for class" + + interceptorBindingClassName, e); + } + ann = AnnotationInstanceProvider.of(annClass); + usedInterceptorBindings.put(interceptorBindingClassName, ann); + } + return ann; + } +}