Skip to content

Commit 5c3bcfb

Browse files
authored
Support quartz v1.x (elastic#2219)
1 parent 5a6f389 commit 5c3bcfb

File tree

24 files changed

+939
-271
lines changed

24 files changed

+939
-271
lines changed

CHANGELOG.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ by the agent may be different. This was done in order to improve the integration
5151
* Add support to Jakarta EE for JAX-WS - {pull}2247[#2247]
5252
* Add support to Jakarta EE for JAX-RS - {pull}2248[#2248]
5353
* Add support for Jakarta EE EJB annotations `@Schedule`, `@Schedules` - {pull}2250[#2250]
54+
* Added support to Quartz 1.x - {pull}2219[#2219]
5455
5556
[float]
5657
===== Performance improvements
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>apm-quartz-job-plugin</artifactId>
7+
<groupId>co.elastic.apm</groupId>
8+
<version>1.26.1-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>apm-quartz-1-plugin</artifactId>
13+
<name>${project.groupId}:${project.artifactId}</name>
14+
15+
<properties>
16+
<version.quartz>1.7.3</version.quartz>
17+
<apm-agent-parent.base.dir>${project.basedir}/../../..</apm-agent-parent.base.dir>
18+
</properties>
19+
20+
<dependencies>
21+
<dependency>
22+
<groupId>${project.groupId}</groupId>
23+
<artifactId>quartz-common</artifactId>
24+
<version>${project.version}</version>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.quartz-scheduler</groupId>
28+
<artifactId>quartz</artifactId>
29+
<version>${version.quartz}</version>
30+
<scope>provided</scope>
31+
</dependency>
32+
<dependency>
33+
<groupId>${project.groupId}</groupId>
34+
<artifactId>quartz-common</artifactId>
35+
<version>${project.version}</version>
36+
<type>test-jar</type>
37+
<scope>test</scope>
38+
</dependency>
39+
</dependencies>
40+
41+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.quartzjob;
20+
21+
import org.quartz.JobExecutionContext;
22+
23+
import javax.annotation.Nullable;
24+
25+
public class Quartz1JobExecutionContextHandler implements JobExecutionContextHandler<JobExecutionContext> {
26+
@Override
27+
@Nullable
28+
public String getJobDetailKey(JobExecutionContext jobExecutionContext) {
29+
if (jobExecutionContext.getJobDetail() == null) {
30+
return null;
31+
}
32+
return jobExecutionContext.getJobDetail().getKey().toString();
33+
}
34+
35+
@Override
36+
@Nullable
37+
public Object getResult(JobExecutionContext jobExecutionContext) {
38+
return jobExecutionContext.getResult();
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.quartzjob;
20+
21+
import co.elastic.apm.agent.bci.bytebuddy.SimpleMethodSignatureOffsetMappingFactory;
22+
import co.elastic.apm.agent.impl.ElasticApmTracer;
23+
import net.bytebuddy.asm.Advice;
24+
import net.bytebuddy.description.method.MethodDescription;
25+
import net.bytebuddy.matcher.ElementMatcher;
26+
import org.quartz.JobExecutionContext;
27+
28+
import javax.annotation.Nullable;
29+
30+
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
31+
import static net.bytebuddy.matcher.ElementMatchers.named;
32+
import static net.bytebuddy.matcher.ElementMatchers.not;
33+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
34+
35+
public class Quartz1JobTransactionNameInstrumentation extends AbstractJobTransactionNameInstrumentation {
36+
public Quartz1JobTransactionNameInstrumentation(ElasticApmTracer tracer) {
37+
super(tracer);
38+
}
39+
40+
@Override
41+
public ElementMatcher<? super MethodDescription> getMethodMatcher() {
42+
return named("execute").or(named("executeInternal"))
43+
.and(takesArgument(0, named("org.quartz.JobExecutionContext").and(not(isInterface()))));
44+
}
45+
46+
public static class AdviceClass extends BaseAdvice {
47+
private static final JobExecutionContextHandler<JobExecutionContext> helper;
48+
49+
static {
50+
helper = new Quartz1JobExecutionContextHandler();
51+
}
52+
53+
@Nullable
54+
@Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
55+
public static Object setTransactionName(@Advice.Argument(value = 0) @Nullable JobExecutionContext jobExecutionContext,
56+
@SimpleMethodSignatureOffsetMappingFactory.SimpleMethodSignature String signature,
57+
@Advice.Origin Class<?> clazz) {
58+
return BaseAdvice.createAndActivateTransaction(jobExecutionContext, signature, clazz, helper);
59+
}
60+
61+
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
62+
public static void onMethodExitException(@Advice.Argument(value = 0) @Nullable JobExecutionContext jobExecutionContext,
63+
@Advice.Enter @Nullable Object transactionObj,
64+
@Advice.Thrown @Nullable Throwable t) {
65+
BaseAdvice.endTransaction(jobExecutionContext, transactionObj, t, helper);
66+
}
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
co.elastic.apm.agent.quartzjob.Quartz1JobTransactionNameInstrumentation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.quartzjob;
20+
21+
import co.elastic.apm.agent.impl.ElasticApmTracer;
22+
import co.elastic.apm.agent.impl.transaction.Outcome;
23+
import co.elastic.apm.agent.impl.transaction.Transaction;
24+
import org.quartz.Job;
25+
import org.quartz.JobDetail;
26+
import org.quartz.JobExecutionContext;
27+
import org.quartz.JobExecutionException;
28+
import org.quartz.SimpleTrigger;
29+
30+
import javax.annotation.Nullable;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.awaitility.Awaitility.await;
34+
35+
class Quartz1JobTransactionNameInstrumentationTest extends AbstractJobTransactionNameInstrumentationTest {
36+
37+
@Override
38+
public Transaction verifyTransactionFromJobDetails(JobDetail job, Outcome expectedOutcome) {
39+
reporter.awaitTransactionCount(1);
40+
41+
Transaction transaction = reporter.getFirstTransaction();
42+
await().untilAsserted(() -> assertThat(reporter.getTransactions().size()).isEqualTo(1));
43+
44+
verifyTransaction(transaction, String.format("%s.%s", job.getKey().getGroup(), job.getKey().getName()));
45+
46+
assertThat(transaction.getOutcome()).isEqualTo(expectedOutcome);
47+
return transaction;
48+
}
49+
50+
public SimpleTrigger createTrigger() {
51+
return new SimpleTrigger("myTrigger", 0, 1 * 1000);
52+
}
53+
54+
@Override
55+
public String quartzVersion() {
56+
return "1.7.3";
57+
}
58+
59+
@Override
60+
JobDetail buildJobDetailTestJob(String name, @Nullable String groupName) {
61+
if (groupName == null) {
62+
return buildJobDetail(TestJob.class, name);
63+
}
64+
return buildJobDetail(TestJob.class, name, groupName);
65+
}
66+
67+
@Override
68+
void executeTestJobCreatingSpan(ElasticApmTracer tracer, boolean traced) throws JobExecutionException {
69+
new TestJobCreatingSpan(tracer, traced).execute(null);
70+
}
71+
72+
@Override
73+
JobDetail buildJobDetailTestJobWithResult(String name) {
74+
return buildJobDetail(TestJobWithResult.class, name);
75+
}
76+
77+
@Override
78+
JobDetail buildJobDetailTestJobWithException(String name) {
79+
return buildJobDetail(TestJobWithException.class, name);
80+
}
81+
82+
@Override
83+
JobDetail buildJobDetailTestSpringJob(String name, String groupName) {
84+
throw new UnsupportedOperationException("Spring scheduling work with updated version of quartz.");
85+
}
86+
87+
@Override
88+
boolean ignoreTestSpringJob() {
89+
// Spring scheduling work with updated version of quartz(2.*.*).
90+
return true;
91+
}
92+
93+
@Override
94+
boolean ignoreDirectoryScanTest() {
95+
// DirectoryScanJob not present at this version
96+
return true;
97+
}
98+
99+
private JobDetail buildJobDetail(Class jobClass, String name) {
100+
JobDetail job = new JobDetail();
101+
job.setName(name);
102+
job.setJobClass(jobClass);
103+
return job;
104+
}
105+
106+
private JobDetail buildJobDetail(Class jobClass, String name, String groupName) {
107+
JobDetail job = new JobDetail();
108+
job.setName(name);
109+
job.setJobClass(jobClass);
110+
job.setGroup(groupName);
111+
return job;
112+
}
113+
114+
public static class TestJob implements Job {
115+
@Override
116+
public void execute(JobExecutionContext context) throws JobExecutionException {
117+
}
118+
}
119+
120+
public static class TestJobCreatingSpan implements Job {
121+
private final ElasticApmTracer tracer;
122+
private final boolean traced;
123+
124+
public TestJobCreatingSpan(ElasticApmTracer tracer, boolean traced) {
125+
this.tracer = tracer;
126+
this.traced = traced;
127+
}
128+
129+
@Override
130+
public void execute(JobExecutionContext context) throws JobExecutionException {
131+
Transaction transaction = tracer.currentTransaction();
132+
if (traced) {
133+
assertThat(transaction).isNotNull();
134+
transaction.createSpan().end();
135+
} else {
136+
assertThat(transaction).isNull();
137+
assertThat(tracer.getActive()).isNull();
138+
}
139+
}
140+
}
141+
142+
public static class TestJobWithResult implements Job {
143+
@Override
144+
public void execute(JobExecutionContext context) throws JobExecutionException {
145+
context.setResult("this is the result");
146+
}
147+
}
148+
149+
public static class TestJobWithException implements Job {
150+
@Override
151+
public void execute(JobExecutionContext context) throws JobExecutionException {
152+
throw new JobExecutionException("intentional job exception");
153+
}
154+
}
155+
156+
157+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>apm-quartz-job-plugin</artifactId>
7+
<groupId>co.elastic.apm</groupId>
8+
<version>1.26.1-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>apm-quartz-2-plugin</artifactId>
13+
<name>${project.groupId}:${project.artifactId}</name>
14+
15+
<properties>
16+
<version.quartz>2.3.1</version.quartz>
17+
<apm-agent-parent.base.dir>${project.basedir}/../../..</apm-agent-parent.base.dir>
18+
</properties>
19+
20+
<dependencies>
21+
<dependency>
22+
<groupId>${project.groupId}</groupId>
23+
<artifactId>quartz-common</artifactId>
24+
<version>${project.version}</version>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.quartz-scheduler</groupId>
28+
<artifactId>quartz</artifactId>
29+
<version>${version.quartz}</version>
30+
<scope>provided</scope>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.springframework</groupId>
34+
<artifactId>spring-context-support</artifactId>
35+
<version>${version.spring}</version>
36+
<scope>test</scope>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.quartz-scheduler</groupId>
40+
<artifactId>quartz-jobs</artifactId>
41+
<version>${version.quartz}</version>
42+
<scope>test</scope>
43+
</dependency>
44+
<dependency>
45+
<groupId>${project.groupId}</groupId>
46+
<artifactId>quartz-common</artifactId>
47+
<version>${project.version}</version>
48+
<type>test-jar</type>
49+
<scope>test</scope>
50+
</dependency>
51+
</dependencies>
52+
</project>

0 commit comments

Comments
 (0)