Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create resettable class file transformers that allow to undo the effe…
…ct of a registered class file transformer.
- Loading branch information
Showing
9 changed files
with
844 additions
and
147 deletions.
There are no files selected for viewing
238 changes: 147 additions & 91 deletions
238
byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/AgentBuilder.java
Large diffs are not rendered by default.
Oops, something went wrong.
157 changes: 157 additions & 0 deletions
157
byte-buddy-dep/src/main/java/net/bytebuddy/agent/builder/ResettableClassFileTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,157 @@ | |||
package net.bytebuddy.agent.builder; | |||
|
|||
import java.lang.instrument.ClassFileTransformer; | |||
import java.lang.instrument.Instrumentation; | |||
import java.util.Collections; | |||
import java.util.Map; | |||
|
|||
/** | |||
* A class file transformer that can reset its transformation. | |||
*/ | |||
public interface ResettableClassFileTransformer extends ClassFileTransformer { | |||
|
|||
/** | |||
* <p> | |||
* Deregisters this class file transformer and redefines any transformed class to its state without this | |||
* class file transformer applied, if the supplied redefinition strategy is enabled. | |||
* </p> | |||
* <p> | |||
* <b>Important</b>: Most JVMs do not support changes of a class's structure after a class was already | |||
* loaded. Therefore, it is typically required that this class file transformer was built while enabling | |||
* {@link AgentBuilder#disableClassFormatChanges()}. | |||
* </p> | |||
* | |||
* @param instrumentation The instrumentation instance from which to deregister the transformer. | |||
* @param redefinitionStrategy The redefinition to apply. | |||
* @return A representation of the result of resetting this transformer. | |||
*/ | |||
Reset reset(Instrumentation instrumentation, AgentBuilder.RedefinitionStrategy redefinitionStrategy); | |||
|
|||
/** | |||
* A result of a class file transformer reset by a {@link ResettableClassFileTransformer}. | |||
*/ | |||
interface Reset { | |||
|
|||
/** | |||
* Determines if this result did apply a deregistration, i.e. the reset class file transformer was not removed previously. | |||
* | |||
* @return {@code true} if the represented reset was applied. | |||
*/ | |||
boolean isApplied(); | |||
|
|||
/** | |||
* Returns a mapping of classes that could not be reset to the errors occured when attempting the reset. If no | |||
* errors occurred, this map is empty. This map is also empty, if this result is not alive. | |||
* | |||
* @return A map containing all errors that occured during a reset. | |||
*/ | |||
Map<Class<?>, Throwable> getErrors(); | |||
|
|||
/** | |||
* A simple result without errors. | |||
*/ | |||
enum Simple implements Reset { | |||
|
|||
/** | |||
* An active result without errors. | |||
*/ | |||
ACTIVE(true), | |||
|
|||
/** | |||
* An inactive result without errors. | |||
*/ | |||
INACTIVE(false); | |||
|
|||
/** | |||
* {@code true} if this result is alive. | |||
*/ | |||
private final boolean alive; | |||
|
|||
/** | |||
* Creates a new simple result. | |||
* | |||
* @param alive {@code true} if this result is alive. | |||
*/ | |||
Simple(boolean alive) { | |||
this.alive = alive; | |||
} | |||
|
|||
@Override | |||
public boolean isApplied() { | |||
return alive; | |||
} | |||
|
|||
@Override | |||
public Map<Class<?>, Throwable> getErrors() { | |||
return Collections.emptyMap(); | |||
} | |||
|
|||
@Override | |||
public String toString() { | |||
return "ResettableClassFileTransformer.Reset.Simple." + name(); | |||
} | |||
} | |||
|
|||
/** | |||
* A result with a map of errors. | |||
*/ | |||
class WithErrors implements Reset { | |||
|
|||
/** | |||
* A map of errors occurred during a class file transformer reset. | |||
*/ | |||
private final Map<Class<?>, Throwable> errors; | |||
|
|||
/** | |||
* Creates a new result with errors. | |||
* | |||
* @param errors A map of errors occurred during a class file transformer reset. | |||
*/ | |||
protected WithErrors(Map<Class<?>, Throwable> errors) { | |||
this.errors = errors; | |||
} | |||
|
|||
/** | |||
* Creates a result of a potentially empty error mapping. | |||
* | |||
* @param errors A map of errors that occurred during a reset. | |||
* @return An appropriate result. | |||
*/ | |||
public static Reset ofPotentiallyErroneous(Map<Class<?>, Throwable> errors) { | |||
return errors.isEmpty() | |||
? Simple.ACTIVE | |||
: new WithErrors(errors); | |||
} | |||
|
|||
@Override | |||
public boolean isApplied() { | |||
return true; | |||
} | |||
|
|||
@Override | |||
public Map<Class<?>, Throwable> getErrors() { | |||
return errors; | |||
} | |||
|
|||
@Override | |||
public boolean equals(Object object) { | |||
if (this == object) return true; | |||
if (object == null || getClass() != object.getClass()) return false; | |||
WithErrors that = (WithErrors) object; | |||
return errors.equals(that.errors); | |||
} | |||
|
|||
@Override | |||
public int hashCode() { | |||
return errors.hashCode(); | |||
} | |||
|
|||
@Override | |||
public String toString() { | |||
return "ResettableClassFileTransformer.Reset.WithErrors{" + | |||
"errors=" + errors + | |||
'}'; | |||
} | |||
} | |||
} | |||
} |
Oops, something went wrong.