From 9680deb6e4fab947c678a38ccd583accdfcb70c7 Mon Sep 17 00:00:00 2001 From: Timm Hirsens Date: Tue, 9 Apr 2019 13:24:29 +0200 Subject: [PATCH 1/4] First MVP Support for Springs @Scheduled Annotation This is a very basic implementation of #418 --- .../apm-spring-scheduled-plugin/pom.xml | 30 +++++ ...heduledTransactionNameInstrumentation.java | 104 ++++++++++++++++++ .../agent/spring/scheduled/package-info.java | 23 ++++ ...ic.apm.agent.bci.ElasticApmInstrumentation | 1 + .../apm/agent/spring/scheduled/Counter.java | 39 +++++++ .../spring/scheduled/ScheduledConfig.java | 30 +++++ ...ledTransactionNameInstrumentationTest.java | 80 ++++++++++++++ apm-agent-plugins/pom.xml | 1 + docs/configuration.asciidoc | 4 +- elastic-apm-agent/pom.xml | 5 + 10 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/pom.xml create mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentation.java create mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java create mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation create mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/Counter.java create mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledConfig.java create mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentationTest.java diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/pom.xml b/apm-agent-plugins/apm-spring-scheduled-plugin/pom.xml new file mode 100644 index 0000000000..7b5166ecf7 --- /dev/null +++ b/apm-agent-plugins/apm-spring-scheduled-plugin/pom.xml @@ -0,0 +1,30 @@ + + + + apm-agent-plugins + co.elastic.apm + 1.5.1-SNAPSHOT + + 4.0.0 + + apm-spring-scheduled-plugin + ${project.groupId}:${project.artifactId} + + + + org.springframework.boot + spring-boot-starter-test + 2.0.2.RELEASE + test + + + org.awaitility + awaitility + 3.1.6 + test + + + + \ No newline at end of file diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentation.java b/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentation.java new file mode 100644 index 0000000000..1fcf283912 --- /dev/null +++ b/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentation.java @@ -0,0 +1,104 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2019 Elastic and contributors + * %% + * 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. + * #L% + */ +package co.elastic.apm.agent.spring.scheduled; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import javax.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import co.elastic.apm.agent.bci.ElasticApmInstrumentation; +import co.elastic.apm.agent.bci.VisibleForAdvice; +import co.elastic.apm.agent.bci.bytebuddy.SimpleMethodSignatureOffsetMappingFactory.SimpleMethodSignature; +import co.elastic.apm.agent.impl.ElasticApmTracer; +import co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration; +import co.elastic.apm.agent.impl.transaction.TraceContext; +import co.elastic.apm.agent.impl.transaction.TraceContextHolder; +import co.elastic.apm.agent.impl.transaction.Transaction; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.isInAnyPackage; +import static net.bytebuddy.matcher.ElementMatchers.declaresMethod; +import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; +import static net.bytebuddy.matcher.ElementMatchers.named; + +public class SpringScheduledTransactionNameInstrumentation extends ElasticApmInstrumentation { + + @VisibleForAdvice + public static final Logger logger = LoggerFactory.getLogger(SpringScheduledTransactionNameInstrumentation.class); + + private Collection applicationPackages = Collections.emptyList(); + + @Advice.OnMethodEnter(suppress = Throwable.class) + private static void setTransactionName(@SimpleMethodSignature String signature, @Advice.Origin Class clazz, @Advice.Local("transaction") Transaction transaction) { + if (tracer != null) { + TraceContextHolder active = tracer.getActive(); + if (active == null) { + transaction = tracer.startTransaction(TraceContext.asRoot(), null, clazz.getClassLoader()) + .withName(signature) + .withType("scheduled") + .activate(); + + } else { + logger.debug("Not creating transaction for method {} because there is already a transaction running ({})", signature, active); + } + } + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void onMethodExit(@Nullable @Advice.Local("transaction") Transaction transaction, + @Advice.Thrown Throwable t) { + if (transaction != null) { + transaction.captureException(t) + .deactivate() + .end(); + } + } + + @Override + public void init(ElasticApmTracer tracer) { + applicationPackages = tracer.getConfig(StacktraceConfiguration.class).getApplicationPackages(); + } + + @Override + public ElementMatcher getTypeMatcher() { + return isInAnyPackage(applicationPackages, ElementMatchers.none()) + .and(declaresMethod(getMethodMatcher())); + } + + @Override + public ElementMatcher getMethodMatcher() { + return isAnnotatedWith(named("org.springframework.scheduling.annotation.Scheduled")); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Arrays.asList("concurrent", "spring-scheduled"); + } +} diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java b/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java new file mode 100644 index 0000000000..7a4e1e4182 --- /dev/null +++ b/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java @@ -0,0 +1,23 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2019 Elastic and contributors + * %% + * 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. + * #L% + */ +@NonnullApi +package co.elastic.apm.agent.spring.scheduled; + +import co.elastic.apm.agent.annotation.NonnullApi; diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation b/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation new file mode 100644 index 0000000000..61fe0bad89 --- /dev/null +++ b/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation @@ -0,0 +1 @@ +co.elastic.apm.agent.spring.scheduled.SpringScheduledTransactionNameInstrumentation \ No newline at end of file diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/Counter.java b/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/Counter.java new file mode 100644 index 0000000000..ebf3fb68b4 --- /dev/null +++ b/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/Counter.java @@ -0,0 +1,39 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2019 Elastic and contributors + * %% + * 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. + * #L% + */ +package co.elastic.apm.agent.spring.scheduled; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class Counter { + private AtomicInteger count = new AtomicInteger(0); + + @Scheduled(fixedDelay = 5) + public void scheduled() { + this.count.incrementAndGet(); + } + + public int getInvocationCount() { + return this.count.get(); + } +} diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledConfig.java b/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledConfig.java new file mode 100644 index 0000000000..2d0fab3724 --- /dev/null +++ b/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledConfig.java @@ -0,0 +1,30 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2019 Elastic and contributors + * %% + * 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. + * #L% + */ +package co.elastic.apm.agent.spring.scheduled; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; + +@Configuration +@EnableScheduling +@ComponentScan("co.elastic.apm.agent.spring.scheduled") +public class ScheduledConfig { +} diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentationTest.java b/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentationTest.java new file mode 100644 index 0000000000..c92ffe494a --- /dev/null +++ b/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentationTest.java @@ -0,0 +1,80 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2019 Elastic and contributors + * %% + * 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. + * #L% + */ +package co.elastic.apm.agent.spring.scheduled; + +import java.util.Collections; + +import org.awaitility.Duration; +import org.junit.BeforeClass; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import co.elastic.apm.agent.MockReporter; +import co.elastic.apm.agent.bci.ElasticApmAgent; +import co.elastic.apm.agent.configuration.SpyConfiguration; +import co.elastic.apm.agent.impl.ElasticApmTracer; +import co.elastic.apm.agent.impl.ElasticApmTracerBuilder; +import net.bytebuddy.agent.ByteBuddyAgent; + +import static org.awaitility.Awaitility.await; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.verify; + +/** + * TODO <> + */ +@SpringJUnitConfig(ScheduledConfig.class) +class SpringScheduledTransactionNameInstrumentationTest { + + private static MockReporter reporter; + private static ElasticApmTracer tracer; + + @BeforeClass + @BeforeAll + static void setUpAll() { + reporter = new MockReporter(); + tracer = new ElasticApmTracerBuilder() + .configurationRegistry(SpyConfiguration.createSpyConfig()) + .reporter(reporter) + .build(); + ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(), + Collections.singletonList(new SpringScheduledTransactionNameInstrumentation())); + } + + @SpyBean + private Counter counter; + + @Test + void testScheduledAnnotatedMethodsAreTraced() { + reporter.reset(); + await() + .atMost(Duration.FIVE_HUNDRED_MILLISECONDS) + .untilAsserted(() -> verify(counter, atLeast(5)).scheduled()); + assertThat(reporter.getTransactions().size(), greaterThanOrEqualTo(counter.getInvocationCount())); + assertThat(reporter.getTransactions().get(0).getName().toString(), containsString("#scheduled")); + } + +} diff --git a/apm-agent-plugins/pom.xml b/apm-agent-plugins/pom.xml index 24b5690968..31a2595a3e 100644 --- a/apm-agent-plugins/pom.xml +++ b/apm-agent-plugins/pom.xml @@ -25,6 +25,7 @@ apm-java-concurrent-plugin apm-urlconnection-plugin apm-jaxws-plugin + apm-spring-scheduled-plugin diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc index 731eddbb9c..960ed9f897 100644 --- a/docs/configuration.asciidoc +++ b/docs/configuration.asciidoc @@ -320,7 +320,7 @@ you should add an additional entry to this list (make sure to also include the d ==== `disable_instrumentations` A list of instrumentations which should be disabled. -Valid options are `annotations`, `apache-httpclient`, `concurrent`, `dispatcher-servlet`, `elasticsearch-restclient`, `executor`, `http-client`, `incubating`, `jax-rs`, `jax-ws`, `jdbc`, `jsf`, `okhttp`, `opentracing`, `public-api`, `render`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `servlet-service-name`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `urlconnection`. +Valid options are `annotations`, `apache-httpclient`, `concurrent`, `dispatcher-servlet`, `elasticsearch-restclient`, `executor`, `http-client`, `incubating`, `jax-rs`, `jax-ws`, `jdbc`, `jsf`, `okhttp`, `opentracing`, `public-api`, `render`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `servlet-service-name`, `spring-mvc`, `spring-resttemplate`, `spring-scheduled`, `spring-service-name`, `urlconnection`. If you want to try out incubating features, set the value to an empty string. @@ -1204,7 +1204,7 @@ If the service name is set explicitly, it overrides all of the above. # sanitize_field_names=password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,authorization,set-cookie # A list of instrumentations which should be disabled. -# Valid options are `annotations`, `apache-httpclient`, `concurrent`, `dispatcher-servlet`, `elasticsearch-restclient`, `executor`, `http-client`, `incubating`, `jax-rs`, `jax-ws`, `jdbc`, `jsf`, `okhttp`, `opentracing`, `public-api`, `render`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `servlet-service-name`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `urlconnection`. +# Valid options are `annotations`, `apache-httpclient`, `concurrent`, `dispatcher-servlet`, `elasticsearch-restclient`, `executor`, `http-client`, `incubating`, `jax-rs`, `jax-ws`, `jdbc`, `jsf`, `okhttp`, `opentracing`, `public-api`, `render`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `servlet-service-name`, `spring-mvc`, `spring-resttemplate`, `spring-scheduled`, `spring-service-name`, `urlconnection`. # If you want to try out incubating features, # set the value to an empty string. # diff --git a/elastic-apm-agent/pom.xml b/elastic-apm-agent/pom.xml index 0611052101..12c6853d3a 100644 --- a/elastic-apm-agent/pom.xml +++ b/elastic-apm-agent/pom.xml @@ -230,6 +230,11 @@ apm-web-plugin ${project.version} + + ${project.groupId} + apm-spring-scheduled-plugin + ${project.version} + From 592f1508c0b07e8061d35d4753e5a504702ac017 Mon Sep 17 00:00:00 2001 From: Timm Hirsens Date: Tue, 9 Apr 2019 15:07:11 +0200 Subject: [PATCH 2/4] Changes for code review --- .../pom.xml | 17 ++- ...eduledTransactionNameInstrumentation.java} | 11 +- .../agent/spring/scheduled/package-info.java | 0 ...ic.apm.agent.bci.ElasticApmInstrumentation | 1 + ...ledTransactionNameInstrumentationTest.java | 106 ++++++++++++++++++ ...ic.apm.agent.bci.ElasticApmInstrumentation | 1 - .../apm/agent/spring/scheduled/Counter.java | 39 ------- .../spring/scheduled/ScheduledConfig.java | 30 ----- ...ledTransactionNameInstrumentationTest.java | 80 ------------- apm-agent-plugins/pom.xml | 2 +- elastic-apm-agent/pom.xml | 2 +- 11 files changed, 125 insertions(+), 164 deletions(-) rename apm-agent-plugins/{apm-spring-scheduled-plugin => apm-scheduled-annotation-plugin}/pom.xml (61%) rename apm-agent-plugins/{apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentation.java => apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java} (89%) rename apm-agent-plugins/{apm-spring-scheduled-plugin => apm-scheduled-annotation-plugin}/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java (100%) create mode 100644 apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation create mode 100644 apm-agent-plugins/apm-scheduled-annotation-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentationTest.java delete mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation delete mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/Counter.java delete mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledConfig.java delete mode 100644 apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentationTest.java diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/pom.xml b/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml similarity index 61% rename from apm-agent-plugins/apm-spring-scheduled-plugin/pom.xml rename to apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml index 7b5166ecf7..9bfcdd866a 100644 --- a/apm-agent-plugins/apm-spring-scheduled-plugin/pom.xml +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml @@ -9,21 +9,20 @@ 4.0.0 - apm-spring-scheduled-plugin + apm-scheduled-annotation-plugin ${project.groupId}:${project.artifactId} - org.springframework.boot - spring-boot-starter-test - 2.0.2.RELEASE - test + org.springframework + spring-context + ${version.spring} - org.awaitility - awaitility - 3.1.6 - test + javax + javaee-api + 7.0 + provided diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentation.java b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java similarity index 89% rename from apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentation.java rename to apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java index 1fcf283912..02aa0b9a99 100644 --- a/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentation.java +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java @@ -48,10 +48,10 @@ import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; import static net.bytebuddy.matcher.ElementMatchers.named; -public class SpringScheduledTransactionNameInstrumentation extends ElasticApmInstrumentation { +public class ScheduledTransactionNameInstrumentation extends ElasticApmInstrumentation { @VisibleForAdvice - public static final Logger logger = LoggerFactory.getLogger(SpringScheduledTransactionNameInstrumentation.class); + public static final Logger logger = LoggerFactory.getLogger(ScheduledTransactionNameInstrumentation.class); private Collection applicationPackages = Collections.emptyList(); @@ -94,7 +94,12 @@ public ElementMatcher getTypeMatcher() { @Override public ElementMatcher getMethodMatcher() { - return isAnnotatedWith(named("org.springframework.scheduling.annotation.Scheduled")); + return isAnnotatedWith( + named("org.springframework.scheduling.annotation.Scheduled") + .or(named("org.springframework.scheduling.annotation.Schedules")) + .or(named("javax.ejb.Schedule")) + .or(named("javax.ejb.Schedule")) + ); } @Override diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java similarity index 100% rename from apm-agent-plugins/apm-spring-scheduled-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java rename to apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/package-info.java diff --git a/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation new file mode 100644 index 0000000000..625072a7aa --- /dev/null +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation @@ -0,0 +1 @@ +co.elastic.apm.agent.spring.scheduled.ScheduledTransactionNameInstrumentation \ No newline at end of file diff --git a/apm-agent-plugins/apm-scheduled-annotation-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentationTest.java b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentationTest.java new file mode 100644 index 0000000000..b672f6b5c8 --- /dev/null +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentationTest.java @@ -0,0 +1,106 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2019 Elastic and contributors + * %% + * 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. + * #L% + */ +package co.elastic.apm.agent.spring.scheduled; + +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.ejb.Schedule; + +import org.junit.BeforeClass; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.scheduling.annotation.Scheduled; + +import co.elastic.apm.agent.MockReporter; +import co.elastic.apm.agent.bci.ElasticApmAgent; +import co.elastic.apm.agent.configuration.SpyConfiguration; +import co.elastic.apm.agent.impl.ElasticApmTracer; +import co.elastic.apm.agent.impl.ElasticApmTracerBuilder; +import net.bytebuddy.agent.ByteBuddyAgent; + +import static org.assertj.core.api.Assertions.assertThat; + + +class ScheduledTransactionNameInstrumentationTest { + + private static MockReporter reporter; + private static ElasticApmTracer tracer; + + @BeforeClass + @BeforeAll + static void setUpAll() { + reporter = new MockReporter(); + tracer = new ElasticApmTracerBuilder() + .configurationRegistry(SpyConfiguration.createSpyConfig()) + .reporter(reporter) + .build(); + ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(), + Collections.singletonList(new ScheduledTransactionNameInstrumentation())); + } + + private SpringCounter springCounter = new SpringCounter(); + private JeeCounter jeeCounter = new JeeCounter(); + @Test + void testSpringScheduledAnnotatedMethodsAreTraced() { + reporter.reset(); + springCounter.scheduled(); + springCounter.scheduled(); + assertThat(reporter.getTransactions().size()).isEqualTo(springCounter.getInvocationCount()); + assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("SpringCounter#scheduled"); + } + + @Test + void testJeeScheduledAnnotatedMethodsAreTraced() { + reporter.reset(); + jeeCounter.scheduled(); + jeeCounter.scheduled(); + assertThat(reporter.getTransactions().size()).isEqualTo(jeeCounter.getInvocationCount()); + assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("JeeCounter#scheduled"); + } + + private class SpringCounter { + private AtomicInteger count = new AtomicInteger(0); + + @Scheduled(fixedDelay = 5) + public void scheduled() { + this.count.incrementAndGet(); + } + + public int getInvocationCount() { + return this.count.get(); + } + } + + private class JeeCounter { + private AtomicInteger count = new AtomicInteger(0); + + @Schedule(minute = "5") + public void scheduled() { + this.count.incrementAndGet(); + } + + public int getInvocationCount() { + return this.count.get(); + } + } + + +} diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation b/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation deleted file mode 100644 index 61fe0bad89..0000000000 --- a/apm-agent-plugins/apm-spring-scheduled-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation +++ /dev/null @@ -1 +0,0 @@ -co.elastic.apm.agent.spring.scheduled.SpringScheduledTransactionNameInstrumentation \ No newline at end of file diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/Counter.java b/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/Counter.java deleted file mode 100644 index ebf3fb68b4..0000000000 --- a/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/Counter.java +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2019 Elastic and contributors - * %% - * 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. - * #L% - */ -package co.elastic.apm.agent.spring.scheduled; - -import java.util.concurrent.atomic.AtomicInteger; - -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -@Component -public class Counter { - private AtomicInteger count = new AtomicInteger(0); - - @Scheduled(fixedDelay = 5) - public void scheduled() { - this.count.incrementAndGet(); - } - - public int getInvocationCount() { - return this.count.get(); - } -} diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledConfig.java b/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledConfig.java deleted file mode 100644 index 2d0fab3724..0000000000 --- a/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2019 Elastic and contributors - * %% - * 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. - * #L% - */ -package co.elastic.apm.agent.spring.scheduled; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.EnableScheduling; - -@Configuration -@EnableScheduling -@ComponentScan("co.elastic.apm.agent.spring.scheduled") -public class ScheduledConfig { -} diff --git a/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentationTest.java b/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentationTest.java deleted file mode 100644 index c92ffe494a..0000000000 --- a/apm-agent-plugins/apm-spring-scheduled-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/SpringScheduledTransactionNameInstrumentationTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2019 Elastic and contributors - * %% - * 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. - * #L% - */ -package co.elastic.apm.agent.spring.scheduled; - -import java.util.Collections; - -import org.awaitility.Duration; -import org.junit.BeforeClass; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; - -import co.elastic.apm.agent.MockReporter; -import co.elastic.apm.agent.bci.ElasticApmAgent; -import co.elastic.apm.agent.configuration.SpyConfiguration; -import co.elastic.apm.agent.impl.ElasticApmTracer; -import co.elastic.apm.agent.impl.ElasticApmTracerBuilder; -import net.bytebuddy.agent.ByteBuddyAgent; - -import static org.awaitility.Awaitility.await; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.verify; - -/** - * TODO <> - */ -@SpringJUnitConfig(ScheduledConfig.class) -class SpringScheduledTransactionNameInstrumentationTest { - - private static MockReporter reporter; - private static ElasticApmTracer tracer; - - @BeforeClass - @BeforeAll - static void setUpAll() { - reporter = new MockReporter(); - tracer = new ElasticApmTracerBuilder() - .configurationRegistry(SpyConfiguration.createSpyConfig()) - .reporter(reporter) - .build(); - ElasticApmAgent.initInstrumentation(tracer, ByteBuddyAgent.install(), - Collections.singletonList(new SpringScheduledTransactionNameInstrumentation())); - } - - @SpyBean - private Counter counter; - - @Test - void testScheduledAnnotatedMethodsAreTraced() { - reporter.reset(); - await() - .atMost(Duration.FIVE_HUNDRED_MILLISECONDS) - .untilAsserted(() -> verify(counter, atLeast(5)).scheduled()); - assertThat(reporter.getTransactions().size(), greaterThanOrEqualTo(counter.getInvocationCount())); - assertThat(reporter.getTransactions().get(0).getName().toString(), containsString("#scheduled")); - } - -} diff --git a/apm-agent-plugins/pom.xml b/apm-agent-plugins/pom.xml index 31a2595a3e..5b8df1d204 100644 --- a/apm-agent-plugins/pom.xml +++ b/apm-agent-plugins/pom.xml @@ -25,7 +25,7 @@ apm-java-concurrent-plugin apm-urlconnection-plugin apm-jaxws-plugin - apm-spring-scheduled-plugin + apm-scheduled-annotation-plugin diff --git a/elastic-apm-agent/pom.xml b/elastic-apm-agent/pom.xml index 12c6853d3a..1074dfd51c 100644 --- a/elastic-apm-agent/pom.xml +++ b/elastic-apm-agent/pom.xml @@ -232,7 +232,7 @@ ${project.groupId} - apm-spring-scheduled-plugin + apm-scheduled-annotation-plugin ${project.version} From d7d6bd83057f0ea3b811ad9b105c21d2684db242 Mon Sep 17 00:00:00 2001 From: Timm Hirsens Date: Wed, 10 Apr 2019 08:45:51 +0200 Subject: [PATCH 3/4] minor fixes --- apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml | 3 ++- .../scheduled/ScheduledTransactionNameInstrumentation.java | 2 +- docs/configuration.asciidoc | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml b/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml index 9bfcdd866a..86d7227432 100644 --- a/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml @@ -17,12 +17,13 @@ org.springframework spring-context ${version.spring} + test javax javaee-api 7.0 - provided + test diff --git a/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java index 02aa0b9a99..cb5d2a594f 100644 --- a/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java @@ -104,6 +104,6 @@ public ElementMatcher getMethodMatcher() { @Override public Collection getInstrumentationGroupNames() { - return Arrays.asList("concurrent", "spring-scheduled"); + return Arrays.asList("concurrent", "scheduled"); } } diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc index 960ed9f897..04c357ca50 100644 --- a/docs/configuration.asciidoc +++ b/docs/configuration.asciidoc @@ -320,7 +320,7 @@ you should add an additional entry to this list (make sure to also include the d ==== `disable_instrumentations` A list of instrumentations which should be disabled. -Valid options are `annotations`, `apache-httpclient`, `concurrent`, `dispatcher-servlet`, `elasticsearch-restclient`, `executor`, `http-client`, `incubating`, `jax-rs`, `jax-ws`, `jdbc`, `jsf`, `okhttp`, `opentracing`, `public-api`, `render`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `servlet-service-name`, `spring-mvc`, `spring-resttemplate`, `spring-scheduled`, `spring-service-name`, `urlconnection`. +Valid options are `annotations`, `apache-httpclient`, `concurrent`, `dispatcher-servlet`, `elasticsearch-restclient`, `executor`, `http-client`, `incubating`, `jax-rs`, `jax-ws`, `jdbc`, `jsf`, `okhttp`, `opentracing`, `public-api`, `render`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `servlet-service-name`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `urlconnection`. If you want to try out incubating features, set the value to an empty string. @@ -1204,7 +1204,7 @@ If the service name is set explicitly, it overrides all of the above. # sanitize_field_names=password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,authorization,set-cookie # A list of instrumentations which should be disabled. -# Valid options are `annotations`, `apache-httpclient`, `concurrent`, `dispatcher-servlet`, `elasticsearch-restclient`, `executor`, `http-client`, `incubating`, `jax-rs`, `jax-ws`, `jdbc`, `jsf`, `okhttp`, `opentracing`, `public-api`, `render`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `servlet-service-name`, `spring-mvc`, `spring-resttemplate`, `spring-scheduled`, `spring-service-name`, `urlconnection`. +# Valid options are `annotations`, `apache-httpclient`, `concurrent`, `dispatcher-servlet`, `elasticsearch-restclient`, `executor`, `http-client`, `incubating`, `jax-rs`, `jax-ws`, `jdbc`, `jsf`, `okhttp`, `opentracing`, `public-api`, `render`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `servlet-service-name`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `urlconnection`. # If you want to try out incubating features, # set the value to an empty string. # From 12413a7ed786269ab18dc678cc07d403af83b019 Mon Sep 17 00:00:00 2001 From: Timm Hirsens Date: Thu, 11 Apr 2019 08:56:10 +0200 Subject: [PATCH 4/4] Tests for repeatable Annotations --- ...heduledTransactionNameInstrumentation.java | 2 +- ...ledTransactionNameInstrumentationTest.java | 59 ++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java index cb5d2a594f..164b960cd0 100644 --- a/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/main/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentation.java @@ -98,7 +98,7 @@ public ElementMatcher getMethodMatcher() { named("org.springframework.scheduling.annotation.Scheduled") .or(named("org.springframework.scheduling.annotation.Schedules")) .or(named("javax.ejb.Schedule")) - .or(named("javax.ejb.Schedule")) + .or(named("javax.ejb.Schedules")) ); } diff --git a/apm-agent-plugins/apm-scheduled-annotation-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentationTest.java b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentationTest.java index b672f6b5c8..ac18c0ff8c 100644 --- a/apm-agent-plugins/apm-scheduled-annotation-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentationTest.java +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin/src/test/java/co/elastic/apm/agent/spring/scheduled/ScheduledTransactionNameInstrumentationTest.java @@ -28,6 +28,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.annotation.Schedules; import co.elastic.apm.agent.MockReporter; import co.elastic.apm.agent.bci.ElasticApmAgent; @@ -56,26 +57,58 @@ static void setUpAll() { Collections.singletonList(new ScheduledTransactionNameInstrumentation())); } - private SpringCounter springCounter = new SpringCounter(); - private JeeCounter jeeCounter = new JeeCounter(); + @Test void testSpringScheduledAnnotatedMethodsAreTraced() { reporter.reset(); + SpringCounter springCounter = new SpringCounter(); springCounter.scheduled(); springCounter.scheduled(); assertThat(reporter.getTransactions().size()).isEqualTo(springCounter.getInvocationCount()); assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("SpringCounter#scheduled"); } + @Test + void testSpringJ8RepeatableScheduledAnnotatedMethodsAreTraced() { + reporter.reset(); + SpringCounter springCounter = new SpringCounter(); + springCounter.scheduledJava8Repeatable(); + springCounter.scheduledJava8Repeatable(); + assertThat(reporter.getTransactions().size()).isEqualTo(springCounter.getInvocationCount()); + assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("SpringCounter#scheduledJava8Repeatable"); + } + + @Test + void testSpringJ7RepeatableScheduledAnnotatedMethodsAreTraced() { + reporter.reset(); + SpringCounter springCounter = new SpringCounter(); + springCounter.scheduledJava7Repeatable(); + springCounter.scheduledJava7Repeatable(); + assertThat(reporter.getTransactions().size()).isEqualTo(springCounter.getInvocationCount()); + assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("SpringCounter#scheduledJava7Repeatable"); + } + @Test void testJeeScheduledAnnotatedMethodsAreTraced() { reporter.reset(); + JeeCounter jeeCounter = new JeeCounter(); jeeCounter.scheduled(); jeeCounter.scheduled(); assertThat(reporter.getTransactions().size()).isEqualTo(jeeCounter.getInvocationCount()); assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("JeeCounter#scheduled"); } + @Test + void testJeeJ7RepeatableScheduledAnnotatedMethodsAreTraced() { + reporter.reset(); + JeeCounter jeeCounter = new JeeCounter(); + jeeCounter.scheduledJava7Repeatable(); + jeeCounter.scheduledJava7Repeatable(); + assertThat(reporter.getTransactions().size()).isEqualTo(jeeCounter.getInvocationCount()); + assertThat(reporter.getTransactions().get(0).getName()).isEqualToIgnoringCase("JeeCounter#scheduledJava7Repeatable"); + } + + private class SpringCounter { private AtomicInteger count = new AtomicInteger(0); @@ -84,6 +117,20 @@ public void scheduled() { this.count.incrementAndGet(); } + @Scheduled(fixedDelay = 5) + @Scheduled(fixedDelay = 10) + public void scheduledJava8Repeatable() { + this.count.incrementAndGet(); + } + + @Schedules({ + @Scheduled(fixedDelay = 5), + @Scheduled(fixedDelay = 10) + }) + public void scheduledJava7Repeatable() { + this.count.incrementAndGet(); + } + public int getInvocationCount() { return this.count.get(); } @@ -97,6 +144,14 @@ public void scheduled() { this.count.incrementAndGet(); } + @javax.ejb.Schedules({ + @Schedule(minute = "5"), + @Schedule(minute = "10") + }) + public void scheduledJava7Repeatable() { + this.count.incrementAndGet(); + } + public int getInvocationCount() { return this.count.get(); }