• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TAKETODAY / today-infrastructure / 18154768944

01 Oct 2025 07:26AM UTC coverage: 81.882% (-0.005%) from 81.887%
18154768944

push

github

web-flow
Merge pull request #290 from TAKETODAY/dev/jspecify

jspecify

59788 of 78013 branches covered (76.64%)

Branch coverage included in aggregate %.

141239 of 167496 relevant lines covered (84.32%)

3.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.79
today-context/src/main/java/infra/instrument/classloading/InstrumentationLoadTimeWeaver.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.instrument.classloading;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.lang.instrument.ClassFileTransformer;
23
import java.lang.instrument.IllegalClassFormatException;
24
import java.lang.instrument.Instrumentation;
25
import java.security.ProtectionDomain;
26
import java.util.ArrayList;
27

28
import infra.instrument.InstrumentationSavingAgent;
29
import infra.lang.Assert;
30
import infra.util.ClassUtils;
31

32
/**
33
 * {@link LoadTimeWeaver} relying on VM {@link Instrumentation}.
34
 * *
35
 * <p><code>-javaagent:path/to/today-instrument-{version}.jar</code>
36
 *
37
 * <p>In Eclipse, for example, add something similar to the following to the
38
 * JVM arguments for the Eclipse "Run configuration":
39
 *
40
 * <p><code>-javaagent:${project_loc}/lib/today-instrument-{version}.jar</code>
41
 *
42
 * @author Rod Johnson
43
 * @author Juergen Hoeller
44
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
45
 * @see InstrumentationSavingAgent
46
 * @since 4.0
47
 */
48
public class InstrumentationLoadTimeWeaver implements LoadTimeWeaver {
49

50
  private static final boolean AGENT_CLASS_PRESENT = ClassUtils.isPresent(
5✔
51
          "infra.instrument.InstrumentationSavingAgent",
52
          InstrumentationLoadTimeWeaver.class.getClassLoader());
1✔
53

54
  @Nullable
55
  private final ClassLoader classLoader;
56

57
  @Nullable
58
  private final Instrumentation instrumentation;
59

60
  final ArrayList<ClassFileTransformer> transformers = new ArrayList<>(4);
6✔
61

62
  /**
63
   * Create a new InstrumentationLoadTimeWeaver for the default ClassLoader.
64
   */
65
  public InstrumentationLoadTimeWeaver() {
66
    this(ClassUtils.getDefaultClassLoader());
3✔
67
  }
1✔
68

69
  /**
70
   * Create a new InstrumentationLoadTimeWeaver for the given ClassLoader.
71
   *
72
   * @param classLoader the ClassLoader that registered transformers are supposed to apply to
73
   */
74
  public InstrumentationLoadTimeWeaver(@Nullable ClassLoader classLoader) {
2✔
75
    this.classLoader = classLoader;
3✔
76
    this.instrumentation = getInstrumentation();
3✔
77
  }
1✔
78

79
  @Override
80
  public void addTransformer(ClassFileTransformer transformer) {
81
    Assert.notNull(transformer, "Transformer is required");
3✔
82
    var actualTransformer = new FilteringClassFileTransformer(transformer, this.classLoader);
7✔
83
    synchronized(this.transformers) {
5✔
84
      Assert.state(this.instrumentation != null,
7!
85
              "Must start with Java agent to use InstrumentationLoadTimeWeaver.");
86
      this.instrumentation.addTransformer(actualTransformer);
4✔
87
      this.transformers.add(actualTransformer);
5✔
88
    }
3✔
89
  }
1✔
90

91
  /**
92
   * We have the ability to weave the current class loader when starting the
93
   * JVM in this way, so the instrumentable class loader will always be the
94
   * current loader.
95
   */
96
  @Override
97
  public ClassLoader getInstrumentableClassLoader() {
98
    Assert.state(this.classLoader != null, "No ClassLoader available");
7!
99
    return this.classLoader;
3✔
100
  }
101

102
  /**
103
   * This implementation always returns a {@link SimpleThrowawayClassLoader}.
104
   */
105
  @Override
106
  public ClassLoader getThrowawayClassLoader() {
107
    return new SimpleThrowawayClassLoader(getInstrumentableClassLoader());
6✔
108
  }
109

110
  /**
111
   * Remove all registered transformers, in inverse order of registration.
112
   */
113
  public void removeTransformers() {
114
    synchronized(this.transformers) {
5✔
115
      if (this.instrumentation != null && !this.transformers.isEmpty()) {
7!
116
        for (int i = this.transformers.size() - 1; i >= 0; i--) {
10✔
117
          this.instrumentation.removeTransformer(this.transformers.get(i));
9✔
118
        }
119
        this.transformers.clear();
3✔
120
      }
121
    }
3✔
122
  }
1✔
123

124
  /**
125
   * Check whether an Instrumentation instance is available for the current VM.
126
   *
127
   * @see #getInstrumentation()
128
   */
129
  public static boolean isInstrumentationAvailable() {
130
    return (getInstrumentation() != null);
6✔
131
  }
132

133
  /**
134
   * Obtain the Instrumentation instance for the current VM, if available.
135
   *
136
   * @return the Instrumentation instance, or {@code null} if none found
137
   * @see #isInstrumentationAvailable()
138
   */
139
  @Nullable
140
  private static Instrumentation getInstrumentation() {
141
    if (AGENT_CLASS_PRESENT) {
2!
142
      return InstrumentationAccessor.getInstrumentation();
2✔
143
    }
144
    else {
145
      return null;
×
146
    }
147
  }
148

149
  /**
150
   * Inner class to avoid InstrumentationSavingAgent dependency.
151
   */
152
  private static final class InstrumentationAccessor {
153

154
    public static Instrumentation getInstrumentation() {
155
      return InstrumentationSavingAgent.getInstrumentation();
2✔
156
    }
157
  }
158

159
  /**
160
   * Decorator that only applies the given target transformer to a specific ClassLoader.
161
   */
162
  private record FilteringClassFileTransformer(
9✔
163
          ClassFileTransformer targetTransformer, @Nullable ClassLoader targetClassLoader) implements ClassFileTransformer {
164

165
    @Override
166
    public byte @Nullable [] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
167
            ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
168

169
      if (this.targetClassLoader != loader) {
4✔
170
        return null;
2✔
171
      }
172
      return this.targetTransformer.transform(
9✔
173
              loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
174
    }
175

176
    @Override
177
    public String toString() {
178
      return "FilteringClassFileTransformer for: " + this.targetTransformer;
×
179
    }
180
  }
181

182
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc